Dev.to WebDev 🛠 Dev 👁 0 📖 3 min read

Resurrecting Flash-era text animation with Kiro: specs, hooks and property tests

Written for the Kiroween Hackathon 2026 (Resurrection category). In the early 2000s, animated text on the web meant Flash-era authoring tools. When Flash was switched off, those effects went dark with it. For Kiroween 2

Written for the Kiroween Hackathon 2026 (Resurrection category).

In the early 2000s, animated text on the web meant Flash-era authoring tools. When Flash was switched off, those effects went dark with it. For Kiroween 2026 I rebuilt that experience for the modern web: Neon Type, a motion-text studio that runs entirely in your browser. Layer text, apply one of 12 effects, and export MP4, GIF or a PNG sequence. No plugin, no backend, no uploads.

This post is about how Kiro shaped the build, because the process turned out to be as interesting as the product.

Specs as a contract

I built it as four Kiro specs: engine-core, effect-presets, exporters and studio. Each went requirements, then design, then tasks, then implementation, and I approved each phase before Kiro moved on. Requirements use EARS-style acceptance criteria, each tagged with how it is verified (unit, property, Playwright or manual). The design documents list numbered correctness properties, and the tasks cite the requirements and properties they satisfy.

The payoff was that disagreements surfaced in documents, not in code. When a dependency choice or a security rule needed to change mid-build (for example how strictly to enforce the Content Security Policy), the change became a spec amendment with its own commit, not a silent edit.

Steering and hooks

Steering files carried the rules Kiro obeyed on every task: TypeScript strict, pnpm workspaces, and above all one that defines the engine: the render function is pure and deterministic, with no Math.random, no Date.now, no performance.now. Four agent hooks enforce the workflow on save: lint, tests, a purity check for those banned calls, and a reminder to update the dependency log whenever package.json changes.

Property-based testing earned its keep

The project has 32 numbered fast-check properties. They found real bugs I would not have found by hand:

  • A validator that rejected valid output sizes. fast-check produced a counterexample (a 17x38 canvas scaled to 4x10) that exposed a wrong aspect-ratio rule.
  • A test mock whose getter-copy silently made a worker backpressure assertion pass without testing anything.
  • A blur that differed between the preview and the export, which led to a deterministic premultiplied-alpha blur so that exports match across browsers.

I told Kiro up front to stop and report on any counterexample instead of working around it, and that rule paid for itself every time.

One code path for preview and export

Because the engine is deterministic, the preview and every export format render frames through the same function, and a Playwright parity test compares them. That test also did something useful while the studio was being built: it showed that text exported from a web worker was measured with fallback fonts, because workers do not share the page's loaded fonts. The mismatch was a measurable pixel difference, exactly what the parity test exists to catch.

What was hard

  • Credits. I ran out of the included credits well before the end. Scoping got tighter, and I cut WebM-with-alpha and animated WebP after finding VP9 alpha unsupported in the Chrome configurations I tested.
  • A strict CSP. One dependency probes for eval. I allowed that single named probe and nothing else.
  • Being specific. Kiro does exactly what the spec says. Vague requirements produced vague results, and precise ones with verification methods produced code I could trust.

What I would tell another builder

Write the correctness properties before the code, make "stop on a counterexample" a standing rule, and keep Kiro's steering short and strict. And keep a parity test between your preview and your output. It will find bugs in places you didn't know to look.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.