ജയശ്രീ - animated stroke trace of the word Jayasree

jayasree · v0.3.0

A self-contained JavaScript library that animates Malayalam script as handwriting - showing how a letter or word is written, stroke by stroke, not just what it looks like. Think Hanzi Writer, for Malayalam.

Try the live demo GitHub

How it works

Everything is static - no server, no font file, no shaping engine in the browser. Glyph outlines are pre-shaped offline, a native speaker records real stroke order for ~290 atoms in a browser recorder, each stroke is refined against the font (pictures below), and composition turns those atoms into ~2000 animatable clusters. ARCHITECTURE.md has the full pipeline walkthrough, file by file.

One stroke through the pipeline

ജ (ja)'s recorded stroke at each stage, over its ghost outline:

ജ: raw recorded stroke
1 · raw, as drawn
ജ: centered stroke
2 · centered onto the ink
ജ: smoothed stroke
3 · smoothed
ജ: straightened stroke
4 · straightened

The full engineering story - including the approaches that didn't work - is in CENTERING_EXPERIMENTS.md.

Use it in your page

import { createStrokeWriter } from "jayasree";

const writer = createStrokeWriter(document.getElementById("stage"));
await writer.load();          // glyph outlines
await writer.loadStrokes();   // hand-authored strokes
await writer.play("നന്ദി", { speed: 1, count: 1 });

Plain ES modules, no build step. Options for pen speed, ink thickness, spacing, and repeat count are documented in the README.

Real-world Malayalam text often spells chillu letters (ൻ, ർ, ൽ, ൾ, ൺ, ൿ) as a 3-codepoint legacy sequence rather than the single modern codepoint - both look identical but need different handling under the hood. play() accepts either automatically; see "Chillu letters: two Unicode encodings, one glyph" in ARCHITECTURE.md.

Contribute

The most valuable contribution is stroke data: a native speaker's hand tracing characters in the browser recorder - no coding required. Code contributions are welcome too; see CONTRIBUTING.md for the ground rules (clean code, tests, and never breaking an existing language's data).