Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Calculate characters per second (CPS) by dividing a clearly defined character count by elapsed time in seconds. In a browser, measure elapsed time with performance.now(), convert milliseconds to seconds, and use total characters divided by total time when combining runs of different lengths.

What counts as a character in JavaScript?

JavaScript’s string.length returns the number of UTF-16 code units, not necessarily the number of Unicode code points or characters a person perceives. Those measures can differ: a supplementary Unicode code point uses two UTF-16 code units, and a visible grapheme can contain multiple code points.

Choose the unit that matches the metric your application promises:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Counting rule JavaScript approach What the count means
UTF-16 code units text.length The string’s length in JavaScript code units; some visible symbols count as more than one.
Unicode code points [...text].length The number of code points yielded by iteration. This still may count a multi-code-point visible symbol more than once.
Grapheme clusters new Intl.Segmenter(locale, { granularity: "grapheme" }) A practical count of user-perceived text units, according to locale-sensitive segmentation.

For example, a flag emoji may be composed of multiple code points but displayed as one grapheme. MDN describes Intl.Segmenter as enabling locale-sensitive segmentation into meaningful items such as graphemes. It is marked Baseline 2024, so older browsers and devices may not support it. Unicode grapheme boundaries are a useful convention, not a universal definition of a character; implementations can tailor segmentation to orthographic conventions or user preferences.

If you use grapheme counts, document the locale and runtime assumptions. Do not switch between code units, code points, and graphemes while comparing results.

How do you calculate CPS for one run?

Let count be the chosen text count and let durationMs be the elapsed duration in milliseconds. Convert the duration to seconds before dividing:

durationSeconds = durationMs / 1000

cps = count / durationSeconds

For browser timing, subtract the start reading from the end reading returned by performance.now(). The result is in milliseconds; dividing the count directly by that number would produce characters per millisecond, not CPS.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function calculateCps(count, durationMs) {
  if (!Number.isFinite(count) || count < 0) {
    throw new RangeError("count must be a finite, non-negative number");
  }
  if (!Number.isFinite(durationMs) || durationMs <= 0) {
    throw new RangeError("durationMs must be a finite, positive number");
  }

  return count / (durationMs / 1000);
}

const startMs = performance.now();
// Run the measurement interval here.
const endMs = performance.now();
const cps = calculateCps(text.length, endMs - startMs);

The validation is part of the calculation logic: a zero or invalid duration cannot produce a meaningful finite rate. If your application should show an error rather than throw, handle that outcome at the call site. The example uses text.length; replace it with the chosen code-point or grapheme count when that is the product’s definition.

Why use performance.now() for elapsed time?

performance.now() returns a high-resolution timestamp in milliseconds relative to a monotonic time origin. It is designed for measuring elapsed intervals and is not affected by system clock adjustments. MDN explains the distinction in its Performance: now() method reference.

Date.now() returns milliseconds since the Unix epoch, which is useful when you need a wall-clock timestamp. It can be affected by system clock changes, so it is generally a poorer choice for measuring a browser interval. MDN documents its behavior in the Date.now() reference.

Timer precision can be reduced by browser privacy and security protections. Browser behavior can also differ on whether the performance clock advances while the operating system is asleep. For ordinary short intervals, performance.now() is the appropriate elapsed-time source; for long measurements that may span sleep, define whether sleep time belongs in the measured duration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How should you combine runs with different durations?

For runs with counts cᵢ and durations tᵢ in seconds, calculate the overall rate from totals:

aggregate CPS = Σcᵢ / Σtᵢ

This is equivalent to a duration-weighted mean of the individual run rates. A run lasting longer contributes proportionally more time and count. The simple arithmetic mean of run-level CPS values gives every run equal weight instead, so it answers a different question when durations vary.

function aggregateCps(runs) {
  const totalCount = runs.reduce((sum, run) => sum + run.count, 0);
  const totalDurationMs = runs.reduce((sum, run) => sum + run.durationMs, 0);

  return calculateCps(totalCount, totalDurationMs);
}

Use this aggregate when the desired result is the rate across all measured time. If you instead want each attempt to have equal influence regardless of duration, calculate and label the unweighted mean explicitly.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How can you make a CPS measurement repeatable?

Keep the arithmetic separate from clock access. A pure function that accepts a count and duration can be tested with fixed values, while live measurement code supplies readings from performance.now().

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Fix the input text. Use the same exact string for each comparison and record it with the result.
  2. Declare the counting rule. State whether you count UTF-16 code units, code points, or grapheme clusters; note locale and runtime where relevant.
  3. Define the interval. Specify when timing starts and ends, and whether pauses are included. Excluding pauses measures active rate; including them measures elapsed-session rate.
  4. Test arithmetic with fixed values. Pass predetermined counts and durations to the pure calculation function instead of relying on a live clock in unit tests.
  5. Keep live timing at the boundary. Read performance.now() when the measured interval begins and ends, then pass the difference into the calculation.

These choices make the result interpretable: changing the text, count definition, pause policy, or timing interval changes what the reported CPS means.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.