Skip to main content
GrN.dk

Main navigation

  • Articles
  • Cases
  • Contact
  • Your Digital Project Manager
  • About Greg Nowak
  • Services
  • Portfolio
  • Container
    • Excel Freelancer
    • Kubuntu - tips and tricks
    • Linux Apache MySQL and PHP
    • News
    • Image Gallery
User account menu
  • Log in

Join my community / free newsletter — sign up here

Breadcrumb

  1. Home
  2. Cases

Learning to write Thai, one traced letter at a time

Client
GrN.dk — internal build
Sector
Educational apps for children
Period
July 2026

abc.grn.dk ↗

The abc.grn.dk alphabet screen: all 44 Thai consonants as cards colour-coded by class — mid blue, high orange, low green — each with its romanized name, above a practice-range picker and a "Practice 44 letters" button.

At a glance

abc.grn.dk teaches a child to write an alphabet rather than to recognise one: pick a letter, trace it with a finger, and the app scores the shape you actually drew and tells you how close you got. Thai first — all 44 consonants, free — with Japanese hiragana and katakana behind a premium gate. Everything happens in the browser: the handwriting recogniser is plain Dart running on the device, the spoken letter names are files bundled with the app, and there is no account, no runtime backend and no advertising.

44 Thai consonants + 92 Japanese kana, traced rather than tapped
542 bundled voice clips — no dependence on a device Thai voice
58 tests green · 3,176 lines of Dart · 3 packages beyond Flutter

The challenge

Most apps that promise to “teach the alphabet” actually test recognition — hear a letter, tap the letter. Writing is the harder half, and it is the half a five-year-old is really practising. Building that means solving a scoring problem: judge the shape a child drew, without punishing them for drawing the strokes in a different order or a different direction, and without sending a child's drawings to a server.

Thai adds its own demands. There are 44 consonants that come in three tone classes a learner is expected to internalise, and each is named by an acronym — ก is gor gai, กอ ไก่, “gor as in chicken”. An early version spoke those names with the device's own text-to-speech, which failed in the obvious way: a phone with no Thai voice reads the names in English, or says nothing at all.

And then there is the audience. The first difficulty bars — 60%, 75% and 90% similarity to pass — were set by adults and turned out to be far too strict for the small hands the app is for.

The solution

abc.grn.dk is a Flutter web app: 3,176 lines of Dart across 18 files, with three packages beyond Flutter itself. No runtime backend exists — progress lives in the browser's localStorage.

Scoring the handwriting. The recogniser is a 204-line, pure-Dart implementation of the $P point-cloud recognizer (Vatavu, Anthony & Wobbrock, 2012). It flattens whatever the child drew into an unordered cloud of points, resamples it to exactly 32, and matches it against the one target letter — not against all 44, because the app already knows which letter it asked for. Treating strokes as a cloud rather than a sequence is what makes it order- and direction-independent, which is exactly the forgiveness a beginner needs. The honest trade-off is stated in the repo's own README: a recogniser this forgiving cannot teach stroke order. The reference shapes are the letters' centre-lines, generated from the font outlines, so a dead-on trace scores near-zero distance; a forgiveness floor scaled by each letter's stroke width means fatter letters are judged a little more gently than thin ones.

Making it a game rather than a test. A letter is one shot. Trace it, lift your finger, and it checks itself — no button to press. Pass and it moves on by itself; miss and you spend a heart and get more help on the next attempt: first a bare canvas, then the letter's centre-line as guide dots, then the whole letter faint underneath. Run out of hearts and it offers a gentle start-again, never a failure screen. A pass on the first, help-free attempt extends a streak. The praise is a soft ascending chime and the miss is a mellow two-note tone — deliberately never a buzzer.

Re-tuned against real hands. The difficulty presets were redesigned after use: Easy now passes at 40% with 7 hearts and is the default, Normal at 60% and Hard at 85%, both with 5. Rather than let a default apply silently, the very first run asks the learner to choose, and Settings can change it later. The guide dots got the same treatment — placed by walking the letter's skeleton longest-first and keeping a dot only if it is a full step from every dot already placed, because the earlier per-fragment spacing left a visible blob at every junction, spotted on the live site.

Sound without depending on the device. The letter names and effects are 542 audio clips generated ahead of time and bundled with the app: Thai in two voices — female (Premwadee) and male (Niwat) — at three speeds, Japanese in one voice at three speeds, plus the two feedback tones. Pronunciation is therefore identical on every phone, whether or not it has a Thai voice installed, and a single Sound switch turns all of it off.

Two scripts, one shape. Japanese arrived as data plus assets rather than as a second app: 46 hiragana and 46 katakana in gojūon order, their own generated stroke templates and their own voice clips. The one rule that cannot be bent is that the font used to render a letter must also be the font its templates were generated from — otherwise the guide the child traces is not the shape being scored. Thai uses Noto Sans Thai Looped; the kana use a subset of Klee One, a textbook-style face.

What is paid, and how honestly. Japanese and a relaxed, un-gamified practice mode sit behind a per-user premium unlock; Thai stays free and there is no advertising anywhere — “no ads” is a compile-time constant referenced wherever an ad slot could plausibly be added, so the intent lives in the code rather than in a policy page. The checkout itself is not built yet, so the locked state opens a plainly-worded “coming soon” sheet instead of a button that goes nowhere.

Delivery included the pipeline. Its own virtual host and git repository, TLS, and a poller that watches the repository every ten minutes: a new commit on master builds the web release, stamps the git SHA into the binary, deploys it and purges Cloudflare. Because Flutter's main.dart.js keeps the same filename between builds, the domain also carries a bypass-cache rule — purge-by-URL propagation was losing races with the post-deploy check. The build stamp is printed on the home screen (v10.0.0 · 4533787), so anyone testing can say exactly which build they are looking at.

The results

The app has been live at abc.grn.dk since 19 July 2026 and is currently v10.0.0, with 58 tests green — the recogniser calibration, the game's rules and the screen navigation all covered without a browser or a device in the loop.

It is also a worked example of the delivery model the rest of this portfolio describes: every feature arrived as a Trello card picked up by an AI agent, from the first tracing loop through the Japanese set to the difficulty redesign — 15 commits between 19 and 24 July 2026, each one deployed by the poller within ten minutes of being pushed.

What it is not, it says out loud. English is listed as coming soon, the paid checkout is not built, and the recogniser is forgiving by design and so will not teach stroke order — a feature on top of the recogniser, not a data change. Both of the changes that mattered most, the dot spacing and the difficulty bars, came from watching the live app rather than from the plan, which is the argument for shipping small and often on a domain of its own.

Draw the letter — it checks itself.

The instruction line at the bottom of every practice screen
  • Flutter
  • language learning
  • text recognition
  • Custom Development
  • AI agents

Got a project that needs the same kind of hands-on delivery?

Your digital project manager

Review Greg on Google

Greg Nowak Google Reviews

 

Illustrated infographic summarizing: Drupal 12 Slipped to December. Drupal 10 Still Runs Out of Road
Drupal 12 Slipped to December. Drupal 10 Still Runs Out of Road
2026-08-07

Drupal 12 arrives as Drupal 10 support ends in December 2026. Moving to Drupal 11.3+ first keeps two mandatory upgrades manageable.

Illustrated infographic summarizing: EU OpenAI Residency Is a Migration Project, Not a Dashboard Toggle
EU OpenAI Residency Is a Migration Project, Not a Dashboard Toggle
2026-08-05

An EU-resident OpenAI API setup needs a new project, regional routing, dependency and state migration, compatibility testing, and clear governance evidence.

Illustrated infographic summarizing: AI Images Need a Chain of Custody, Not Just a Disclosure Label
AI Images Need a Chain of Custody, Not Just a Disclosure Label
2026-08-04

AI image labels are only the endpoint. Learn how to test C2PA credentials through editing, CMS, CDN and agency handoffs while preserving evidence.

Illustrated infographic summarizing: MCP Just Went Stateless: Audit the Integrations Behind Your AI Tools
MCP Just Went Stateless: Audit the Integrations Behind Your AI Tools
2026-08-03

The 28 July 2026 MCP release removes protocol sessions and changes discovery, tasks, caching, OAuth and tracing. A practical guide to auditing the move.

Illustrated infographic summarizing: SEO Trends for 2026: What Actually Changed Since 2024
SEO Trends for 2026: What Actually Changed Since 2024
2026-08-03

A practical guide to what changed in SEO between 2024 and 2026, from AI and multimodal search to Core Web Vitals, privacy and local visibility.

Illustrated infographic summarizing: INP and Green SEO Share a Backlog: Cut the Work Every Visit Repeats
INP and Green SEO Share a Backlog: Cut the Work Every Visit Repeats
2026-08-03

INP and sustainable web work often expose the same waste. Use field data, profiling, caching and performance budgets to build one practical backlog.

Illustrated infographic summarizing: AI crawler policy now has verbs: separate search, RAG, and training
AI crawler policy now has verbs: separate search, RAG, and training
2026-08-02

AI crawler rules now need separate decisions for search, RAG, and training, backed by practical testing across robots.txt, CDNs, WAFs, and CMS controls.

Illustrated infographic summarizing: WordPress Supports Old PHP; Your Production Server Shouldn’t
WordPress Supports Old PHP; Your Production Server Shouldn’t
2026-08-01

WordPress still runs on legacy PHP, but compatibility is not a security policy. Build and test your upgrade path before PHP 8.2 support ends.

Illustrated infographic summarizing: The AI-built tool your team relies on needs an owner
The AI-built tool your team relies on needs an owner
2026-07-31

AI-built internal tools can become business-critical before anyone owns them. Here is how to secure, review, monitor, and retire them without blocking useful work.

Illustrated infographic summarizing: Your AI model has an expiry date: build the migration lane now
Your AI model has an expiry date: build the migration lane now
2026-07-30

AI models retire on a schedule. Learn how to map dependencies, test replacements, release safely and preserve a working rollback route.

More articles
RSS feed

Footer

  • All articles
  • Contact

GrN.dk web platforms, web optimization, data analysis, data handling and logistics.