cd /news/ai-tools/i-m-building-a-scripting-language-fo… Β· home β€Ί topics β€Ί ai-tools β€Ί article
[ARTICLE Β· art-134478] src=dev.to β†— pub= topic=ai-tools verified=true sentiment=Β· neutral

I'm building a scripting language for whiteboard animations

A developer built Strokeline, a scripting language for whiteboard animations that lets users describe scenes in text and have them rendered as hand-drawn animations, with scripts simple enough for AI models like ChatGPT or Claude to generate. The project's development was marked by debugging challenges, including a parser error-recovery loop that consumed 4GB of RAM, a React and Zustand state-management cycle causing infinite re-renders, and a bug where typed text vanished from the canvas after rendering.

by read7 min views3 publishedSep 19, 2026

I started Strokeline with one dumb, simple idea:

What if you could describe an animation the same way you describe a webpage β€” with text β€” and something else just... drew it?

Not AI-generated video.

Not a drag-and-drop editor where you fight with alignment guides for twenty minutes.

A script.

You write:

CIRCLE browser
RECTANGLE server
ARROW browser -> server

Hit run, and a hand-drawn whiteboard animation plays.

The twist β€” and really the whole point β€” is that the scripting language is simple enough that you can ask ChatGPT, Claude, or another AI to write it for you.

Something like:

Explain how DNS works in 45 seconds.

goes in.

A script comes out.

You paste it in.

You hit run.

And hopefully...

an animation comes out.

Simple idea.

Turns out "simple idea" and "simple to build" have almost nothing to do with each other.

Somewhere in week one, my test suite died.

Not failed.

Died.

Vitest ran out of memory and took the whole process down with it.

It took a while to track down, but here's what was happening.

My parser had a recovery mechanism.

If it hit a broken line, it was supposed to skip ahead to the next recognizable keyword and keep going instead of crashing on the first typo.

Good idea in theory.

The bug was that one specific keyword β€” TEXT, if you're curious β€” could be interpreted in two different ways.

It could be a property:

CREATE server AS RECTANGLE
  TEXT "SERVER"
END

But it was also being treated as a statement boundary that the error-recovery logic could search for.

Under the wrong conditions, the parser would "recover" to a position it had never actually left.

So it did this:

parse
  ↓
error
  ↓
recover
  ↓
same position
  ↓
error
  ↓
recover
  ↓
same position
  ↓
...

Forever.

Every iteration created another diagnostic object.

Eventually:

4GB of RAM.

Then the OS gave up.

The actual fix was three lines.

Finding those three lines was not.

Once the parser was solid, I started building the actual editor.

The basic idea is pretty simple:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     β”‚                          β”‚
β”‚    SCRIPT EDITOR    β”‚      ANIMATION           β”‚
β”‚                     β”‚                          β”‚
β”‚  CREATE browser...  β”‚       ✏️ β—‹ ───────→ β–‘   β”‚
β”‚                     β”‚                          β”‚
β”‚                     β”‚                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Type on the left.

Watch it animate on the right.

I pasted in a two-scene test script to check everything worked end to end.

Black screen.

Console full of red text.

Maximum update depth exceeded.

Great.

This one wasn't the parser at all.

It was React and my state manager, Zustand, getting into a fight neither of them could win.

Somewhere in the code that renders the scene tabs, a piece of derived data β€” basically:

"Give me the list of scenes."

β€” was being recalculated on every render.

That meant it was a new array every time, even when nothing had actually changed.

React compares objects and arrays by reference.

So:

new array
   ↓
"Something changed"
   ↓
re-render
   ↓
calculate scenes
   ↓
new array
   ↓
"Something changed"
   ↓
re-render
   ↓
...

It only showed up with two or more scenes.

One scene?

Totally fine.

Two scenes?

Welcome to hell.

That's the kind of bug that makes you paranoid about every other component in your codebase doing the exact same thing quietly, waiting for the right input.

This one's my favorite because it's so specific that it's almost funny.

I finally got the "hand-drawn" reveal working.

Text types itself onto the canvas character by character.

Shapes trace their own outlines like a pen is drawing them.

It was genuinely satisfying to watch.

Except...

A few seconds after any text finished typing, it would just...

disappear.

Not fade.

Not glitch.

Gone.

Like it was never there.

I had two separate rendering paths.

One for:

still animating

where the text was drawn live every frame.

And another for:

finished

where I could reuse a cached image instead of asking an expensive hand-drawn rendering library to redo the same work sixty times a second for something that wasn't moving.

Smart idea.

Better performance.

Except the code calculating how large that cached image needed to be was using a rough guess:

text.length * fontSize * 0.55

I am not proud of that line.

It also assumed the text started from a point instead of being centered on that point, which is how it actually gets drawn.

So everything looked perfectly fine while the animation was running.

Then the animation finished.

The renderer switched to:

"just show the cached version"

And suddenly the cached version had the wrong dimensions and the wrong position.

Half the text disappeared silently.

Every.

Single.

Time.

The animation itself was working.

The cache was working.

The transition between the two was the problem.

That's probably the part I find most interesting.

There was no mysterious AI behavior.

No obscure browser API.

No impossible-to-reproduce hardware issue.

Just normal software bugs.

Different bugs.

Different systems.

Different symptoms.

But underneath, they all had something in common:

the program believed something was true when it wasn't.

The parser thought it had recovered.

It hadn't.

React thought the data had changed.

The renderer thought the cached text had the correct dimensions.

It didn't.

Three completely different corners of the codebase.

Three completely different disguises.

Same fundamental problem.

Because I think there's a specific kind of satisfaction in watching something like this get built in public.

Not just the polished announcement at the end.

The bugs too.

The broken builds.

The weird rendering problems.

The moments where you stare at a console error wondering how something this stupid could possibly be happening.

The whole premise of Strokeline is:

text
  ↓
program
  ↓
animation

And building the thing that makes that possible has turned out to be a pretty good example of exactly that process.

You write something you believe is correct.

Then reality β€” the browser, the state manager, the parser, the rendering pipeline β€” tells you:

"No."

And you investigate why.

The interesting thing about a scripting language for animations isn't really the shapes.

Drawing a circle isn't difficult.

Drawing an arrow isn't difficult.

Even animating them isn't particularly difficult.

The interesting part is creating a language that is:

For example, I want something like this to be enough to explain a basic HTTP request:

SCENE 1 "HTTP Request"

  CREATE browser AS CIRCLE
    POSITION 400 500
    RADIUS 100
    LABEL "Browser"
    DRAW 1s
  END

  CREATE server AS RECTANGLE
    POSITION 1400 500
    WIDTH 300
    HEIGHT 220
    TEXT "SERVER"
    DRAW 1s
  END

  ARROW browser -> server
    LABEL "HTTP Request"
    DRAW 1s
  END

END SCENE

That's the direction I'm aiming for.

Not a giant animation framework that requires a 400-page manual.

Something closer to:

HTML, but for explaining things visually.

This is where Strokeline gets particularly interesting to me.

You don't necessarily need to learn the scripting language.

You could simply ask:

Explain how a database index works in 60 seconds.

Use three scenes.

Start with a slow query.

Then show the index.

Finish by comparing indexed and non-indexed lookups.

The AI generates the script.

Strokeline validates it.

The renderer executes it.

And you get an animation.

That means the language becomes a kind of intermediate representation for visual explanations.

The AI doesn't have to generate pixels.

It generates instructions.

The renderer handles the pixels.

That's a much more interesting problem to me.

The core loop works now:

Write script
    ↓
Parse script
    ↓
Build scene
    ↓
Render animation
    ↓
Play / scrub / inspect

I've also been working on things like:

And yes...

bugs.

A lot of bugs.

But that's part of building it.

If you're the kind of person who reads:

"AI generates a whiteboard animation script and my platform just... plays it."

and immediately thinks:

"Wait, how does the timeline handle scrubbing backward in the middle of an animation without breaking everything?"

You're exactly who I'm building this for.

And I'd genuinely love to hear what you'd worry about first.

I'm still figuring out what Strokeline ultimately becomes.

Right now, I'm focused on getting the fundamentals right:

language β†’ parser β†’ timeline β†’ renderer β†’ animation

Once that foundation is solid, there are a lot of directions this could go.

More animation primitives.

Better camera control.

Reusable components.

Timeline editing.

AI-assisted script generation.

Maybe eventually collaborative editing.

But for now?

I'm happy that I can type a few lines of text and watch the browser turn them into something that actually moves.

Especially after spending several hours debugging why the text was disappearing.

More soon.

Probably with more bugs to tell you about.

── more in #ai-tools 4 stories Β· sorted by recency
── more on @strokeline 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/i-m-building-a-scrip…] indexed:0 read:7min 2026-09-19 Β· β€”