JSON Program Protocol
This document describes the JSON structure that the frontend editor sends to POST /run. Understanding it is useful if you want to build alternative editors, write tests, or debug compilation issues.
Top-level structure
| Field | Description |
|---|---|
version |
Always "1.0" - reserved for future breaking changes |
molds |
Names of all molds whose blocks appear in this program |
program |
Root node - the first block that runs |
Node structure
Every node follows this shape:
{
"id": "node-1",
"mold": "io",
"block": "print",
"params": {
"value": "@score"
},
"next": { ... }
}
| Field | Description |
|---|---|
id |
Unique string identifier for this node (assigned by the canvas) |
mold |
Mold name - e.g. "io", "variables", "webscraper" |
block |
Block id within the mold - e.g. "print", "declare" |
params |
Key-value map of the user's settings for this node |
next |
The next node to execute, or null if this is the last node |
Parameter values
Params are always strings. Two conventions:
| Syntax | Meaning |
|---|---|
"@score" |
Variable reference - resolves to the current value of score |
"hello" |
String literal - used as-is (wrapped in repr() for string contexts) |
"42" |
Numeric literal - used as-is; cast to int/float inside the block handler |
The @ prefix tells the compiler to emit the bare identifier instead of a quoted string.
Linear flow
Most blocks use next to chain:
{
"id": "node-1",
"mold": "variables",
"block": "declare",
"params": { "name": "score", "value": "0", "dataType": "int" },
"next": {
"id": "node-2",
"mold": "io",
"block": "print",
"params": { "value": "@score" },
"next": null
}
}
Branching flow
Decision nodes use branches instead of next:
{
"id": "node-3",
"mold": "control",
"block": "branch",
"params": {
"left": "@score",
"op": ">",
"right": "5"
},
"branches": {
"true": {
"id": "node-4",
"mold": "io",
"block": "print",
"params": { "value": "You win!" },
"next": null
},
"false": {
"id": "node-5",
"mold": "io",
"block": "print",
"params": { "value": "Try again." },
"next": null
}
},
"next": {
"id": "node-6",
"mold": "io",
"block": "print",
"params": { "value": "Game over." },
"next": null
}
}
Both branches converge on next (if provided) after they finish executing.
Supported operators: >, <, >=, <=, ==, !=
Loop flow
Loop nodes use body for the repeated block chain and next for after the loop:
{
"id": "node-7",
"mold": "control",
"block": "forLoop",
"params": {
"variable": "i",
"start": "0",
"end": "10",
"step": "1"
},
"body": {
"id": "node-8",
"mold": "io",
"block": "print",
"params": { "value": "@i" },
"next": null
},
"next": {
"id": "node-9",
"mold": "io",
"block": "print",
"params": { "value": "Done!" },
"next": null
}
}
Function definition
Function definition nodes are similar to loops - body holds the function body:
{
"id": "node-10",
"mold": "functions",
"block": "define",
"params": {
"name": "greet",
"args": "name"
},
"body": {
"id": "node-11",
"mold": "io",
"block": "print",
"params": { "value": "@name" },
"next": null
},
"next": {
"id": "node-12",
"mold": "functions",
"block": "call",
"params": { "name": "greet", "args": "Alice", "result": "_" },
"next": null
}
}
Full program example
Declare a variable, loop 3 times, print the counter each iteration:
{
"version": "1.0",
"molds": ["variables", "control", "io"],
"program": {
"id": "node-1",
"mold": "variables",
"block": "declare",
"params": { "name": "count", "value": "0", "dataType": "int" },
"next": {
"id": "node-2",
"mold": "control",
"block": "forLoop",
"params": { "variable": "i", "start": "1", "end": "4", "step": "1" },
"body": {
"id": "node-3",
"mold": "io",
"block": "print",
"params": { "value": "@i" },
"next": null
},
"next": null
}
}
}
Sends to POST /run and returns:
{
"ok": true,
"output": "1\n2\n3\n",
"highlights": ["node-1", "node-2", "node-3", "node-3", "node-3"]
}
What the compiler does with this
ProgramCompiler.compile(program) walks the tree recursively and emits Python:
import math as _math
import random as _random
# node-1
count = int(0)
# node-2
for i in range(1, 4, 1):
# node-3
print(i)
Each # node-N comment tells the execution tracker which node is currently running, so the editor can highlight it.