English | 한국어
Build block code from plain language. It is built on Scratch, so it can keep pulling in upstream Scratch updates, and it drops onto a project you already built on Scratch without much work.
▶ Try it live.
A child types out whatever idea comes to mind. A sentence like "make the cat spin forever and say hi" is enough. The AI turns the idea into Scratch blocks and hands it back as code. Run it, and if it is not what you wanted, ask for another change or drag the blocks around yourself.
- Talk to build. A sentence becomes block code.
- Talk to edit. Ask for a change and only those blocks change. What was already there is kept.
- Loops and key presses.
repeatandforeverwith nesting, pluswhen_keyfor programs you drive from the keyboard. - Chat history. Past turns stay, each with a preview of the blocks it produced.
- Resizable. The panel and the block previews resize to whatever size you want, by mouse or touch.
- Edit by hand. Run the blocks the AI wrote and change them however you like.
Pick how the app reaches the language model. The hosted demo runs on Claude Haiku 4.5 with a fixed daily usage cap. You can enter your own LLM API key to use the model you prefer, or wire up a custom server so this can sit on top of a Scratch-based service you already run.
| Mode | What it does | Needs |
|---|---|---|
| Free | For trying it out. | Nothing. |
| My key (BYOK) | Calls the Anthropic API directly from the browser. | An Anthropic API key, stored only in your browser. |
| Custom server | Sends requests to an API server you build yourself. | Your own endpoint. |
Requires Node 18 (.nvmrc pins 18.20.8).
nvm use
npm install
NODE_OPTIONS=--openssl-legacy-provider npm startOpen the Scratch page at http://localhost:8601.
Two-way data flow
%%{init: {"flowchart": {"diagramPadding": 100}}}%%
flowchart LR
words(["plain language<br/>(a typed sentence)"]) -- ask --> llm{{"LLM"}}
llm -->|"new DSL, or a diff<br/>(changed parts only)"| dsl["DSL<br/>(intermediate)"]
dsl -- compile --> blocks["blocks"]
blocks -- run --> stage(["stage"])
blocks -- decompile --> dsl
dsl -- edit request --> llm
classDef input fill:#FFBF00,stroke:#CC9900,color:#1A1A1A
classDef model fill:#9966FF,stroke:#774DCB,color:#FFFFFF
classDef ir fill:#5CB1D6,stroke:#2E8EB8,color:#FFFFFF
classDef block fill:#4C97FF,stroke:#3373CC,color:#FFFFFF
classDef result fill:#59C059,stroke:#389438,color:#FFFFFF
class words input
class llm model
class dsl ir
class blocks block
class stage result
A small intermediate language (the DSL) sits between the model and Scratch. The model reads and writes the DSL; the app compiles it to blocks and decompiles blocks back to it. The edit path sends only a diff, and each change carries a fingerprint of the script it targets, so a wrong or stale reference is dropped rather than deleting the wrong blocks.
System architecture
%%{init: {"flowchart": {"diagramPadding": 100}}}%%
flowchart TB
subgraph browser["browser (frontend)"]
ui(["prompt UI<br/>(plain language in)"])
harness["ai-harness<br/>(dsl / edit / llm)"]
gui["Scratch GUI + VM<br/>(edit / run / render)"]
conn{"connection mode<br/>(any model)"}
ui -- words --> harness
harness -- inject blocks --> gui
harness -- LLM call --> conn
end
conn -- free --> proxy(["free proxy server<br/>(for the demo)"])
conn -- my key --> userkey(["your API key<br/>(an LLM API key you own)"])
conn -- custom server --> userserver(["your LLM server<br/>(a server you host)"])
classDef panel fill:#FFBF00,stroke:#CC9900,color:#1A1A1A
classDef core fill:#4C97FF,stroke:#3373CC,color:#FFFFFF
classDef scratch fill:#5CB1D6,stroke:#2E8EB8,color:#FFFFFF
classDef pick fill:#FFAB19,stroke:#CF8B17,color:#1A1A1A
classDef llm fill:#9966FF,stroke:#774DCB,color:#FFFFFF
class ui panel
class harness core
class gui scratch
class conn pick
class proxy,userkey,userserver llm
style browser fill:none,stroke:#8A8A8A,stroke-width:2px,stroke-dasharray:6 4
The harness runs entirely in the browser, with no backend of its own. This project's own code lives in one place, src/lib/ai-harness/. The original Scratch code is left as untouched as possible, which keeps it reusable and makes it easy to keep following changes in the upstream repo.
| Module | Role |
|---|---|
dsl.js |
Compile DSL to blocks, decompile blocks to DSL (round-trip). |
llm.js |
Build prompts, call the model, parse and validate the returned DSL. |
edit.js |
Diff old and new DSL, apply only the changed scripts by fingerprint. |
endpoint-store.js, key-store.js |
Connection mode, endpoint, and key, stored in the browser. |
history-store.js, chat-store.js |
Chat turns and their block previews. |
dsl-to-blockly-xml.js |
Render block previews for the history cards. |
The one place we touch Scratch itself is a two-line mount in src/components/gui/gui.jsx and a dev hook in src/reducers/vm.js.
Built as a fork of scratch-gui (Scratch 3.0) and React, with a language model behind the connection modes above.
Scratch is a project of the Scratch Foundation. Because scratch-gui is licensed under the GNU AGPL v3, this fork is too. See LICENSE and the license compliance notes for the dependency breakdown.
There are 630-odd unit tests and GitHub Actions CI (lint, test, build), so you can check a change with npm test. Run it locally, and keep new AI code under src/lib/ai-harness/ so it stays separable from Scratch. See CONTRIBUTING.md for setup, tests, and pull-request guidelines.

