Building a Free Javanese Script (Aksara Jawa) Converter in Vanilla JavaScript javascript, webdev, i18n, beginners
A few months ago I set out to build something small but surprisingly deep: a converter that turns Latin text into Aksara Jawa — the traditional script of the Javanese language, spoken by ~80 million people in Indonesia.
A few months ago I set out to build something small but surprisingly deep: a converter that turns Latin text into Aksara Jawa — the traditional script of the Javanese language, spoken by ~80 million people in Indonesia. What looked like a simple "map letters to symbols" task turned into a fun lesson in writing systems. Here's what I learned.
You can try the finished tool here: aksarajava.id.
It's not an alphabet — it's an abugida
My first mistake was assuming Javanese script works like the Latin alphabet, one symbol per sound. It doesn't.
Aksara Jawa is an abugida. Each of its 20 base consonants already carries an inherent "a" vowel. So the letter ꦏ isn't "k" — it's "ka". To get other vowels, you attach small marks called sandhangan. To write a bare consonant (no vowel), you add a "killer" mark called pangkon.
That means you can't process the input character by character. You have to break words into syllables first.
A peek at the logic
Here's a simplified version of the core idea — map each syllable to a base glyph plus an optional vowel sign:
const BASE = { k: 'ꦏ', g: 'ꦒ', n: 'ꦤ', m: 'ꦩ', s: 'ꦱ' /* ... */ };
const SANDHANGAN = { i: 'ꦶ', u: 'ꦸ', e: 'ꦺ', o: 'ꦺꦴ' }; // 'a' is inherent
function syllableToJavanese(consonant, vowel) {
let out = BASE[consonant]; // already carries 'a'
if (vowel && vowel !== 'a') {
out += SANDHANGAN[vowel]; // modify the vowel
}
return out;
}
syllableToJavanese('k', 'i'); // ꦏꦶ → "ki"
The real engine handles consonant clusters (pasangan, where a dead consonant stacks under the previous one), numbers, and more — but the mental model above is the heart of it. The whole thing is plain JavaScript, no dependencies, and runs entirely in the browser.
The bug a native speaker caught
Here's my favorite part. After launch, a native Javanese reader pointed out that the word "kanca" (friend) was rendered wrong.
My engine wrote it literally: ka + n + ca. But Javanese has a rule called homorganic nasal assimilation: when an "n" sound comes right before "c" or "j", it changes to "ny" (ꦚ). So the correct form of kanca is ka + nya + ca, not ka + na + ca.
It was a one-line fix once I understood it:
// a syllable-final "n" before a palatal (c/j) becomes "ny"
if (coda === 'n' && (nextOnset === 'c' || nextOnset === 'j')) {
coda = 'ny';
}
Lesson: when you're encoding a human language, the native speakers are your best test suite. I would never have found this edge case on my own.
Privacy by design
One decision I'm happy with: the conversion runs 100% in the browser. Nothing you type is sent to a server. For a tool people use to write their own names and personal messages, that felt important — and it also makes it instant and free to host.
Try it
If you want to see it in action (or write your own name in Javanese script), it's live and free at aksarajava.id. There are also beginner lessons for the script and an interactive quiz.
Writing systems are a fantastic playground for developers — you learn linguistics, Unicode, and font rendering all at once. If you've built anything similar for another script, I'd love to hear about it in the comments. 🙏
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.