demo · v141
Grapheme Clusters
Pull every grapheme cluster out of a string with getTextClusters({ granularity: "grapheme" }) and animate each one independently. Emoji ZWJ sequences, family stacks, regional indicators, combining marks — the platform tells you exactly where one user-perceived character ends and the next begins.
the call
const metrics = ctx.measureText(text);
const clusters = metrics.getTextClusters({ granularity: "grapheme" });
for (const c of clusters) {
const slice = text.slice(c.begin, c.end);
ctx.save();
ctx.translate(originX + c.x + bob(c.begin), originY + c.y);
ctx.fillText(slice, 0, 0);
ctx.restore();
}
why this angle
Counting "characters" in a JavaScript string is a trap. "👨👩👧👦".length is 11 because ZWJ sequences are multiple code points; "🇬🇧".length is 4 because regional indicators are pairs of code points; "café".length is 4 or 5 depending on NFC vs NFD. Animation frameworks, typewriter effects, kinetic typography — anyone wanting to move "one character" at a time — had to ship their own grapheme-breaker. getTextClusters hands it back from the platform's text shaper.