Authoring Dustwright designs
A guide for LLM agents (and people) writing redstone designs in the rsd/1 format for Minecraft Bedrock
Edition, or Java Edition (see Java Edition). Read it once, then work in the loop at the end:
write, validate, fix.
The format in one minute
A design is one JSON file, <name>.rsd.json: horizontal layers of character rows, and a legend that
maps each character to a block.
{
"format": "rsd/1",
"meta": { "name": "NOT gate", "edition": "bedrock", "gameVersion": "1.26.50" },
"legend": { "#": "stone", "L": "lever mount=floor", "-": "dust", "t": "torch mount=west", "*": "lamp" },
"layers": [["#####"], ["L-#t*"]],
"ports": {
"inputs": [{ "name": "A", "pos": [0, 1, 0] }],
"outputs": [{ "name": "out", "pos": [4, 1, 0] }]
},
"tests": [
{ "set": { "A": 0 }, "afterTicks": 4, "expect": { "out": 1 } },
{ "set": { "A": 1 }, "afterTicks": 4, "expect": { "out": 0 } }
]
}
Required keys: format (always "rsd/1"), meta.name, legend, layers. Unknown keys are errors; put
tool-specific data under ext.
Axes and grid
- +X is east, +Y is up, +Z is south.
[0,0,0]is the north-west bottom corner. layers[y]is one horizontal slice, listed bottom to top.layers[y][z]is one row string, listed north to south.- The character at index
xof a row is the block atx, west to east. - So the block at
[x, y, z]islayers[y][z][x]. .and space are air.- Every row in the design has the same length, and every layer has the same number of rows. Pad with
.. - Legend keys are single printable ASCII characters other than
.and space.
Block specs
<type> [key=value ...] with single spaces, all lowercase: repeater facing=east delay=2. Values are
lowercase words, integers or true/false. Unspecified keys take the default, but always write orientation
keys explicitly. A spec is at most 200 characters.
Orientation (identical in the viewer, validator and every exporter)
| Type | Key | Meaning |
|---|---|---|
| repeater, comparator | facing |
Direction the signal exits |
| torch, lever, button | mount |
Where the supporting block is: floor, ceiling, or north/south/east/west |
| piston, sticky_piston | facing |
Direction the head pushes |
| observer | facing |
Direction the face looks (its input side); the output is the opposite side |
| hopper, dropper, dispenser | facing |
Direction items leave |
| stairs | facing, half |
Direction you walk to climb them (the tall back is on that side); half=top is upside down |
| slab | half |
bottom or top half of the cell |
| door | facing |
Direction a player looks when placing it; hinge is left or right |
| trapdoor | facing, half |
The hinge is on the edge opposite facing; half is the bottom or top of the cell when closed |
Examples: a torch on the east face of a block that sits west of it is torch mount=west. A repeater carrying a
signal from west to east is repeater facing=east. Torches have no ceiling mount; levers and buttons do.
Block list
| Type | Category | States (default) | Needs support | Port |
|---|---|---|---|---|
button |
power source | mount floor/ceiling/north/south/east/west (default floor); material stone/wood (default stone); pressed false/true (default false) |
by mount | input |
comparator |
component | facing north/south/east/west (default north); mode compare/subtract (default compare); powered false/true (default false) |
below | output (0..15) |
concrete |
solid | color 16 dye colours, e.g. white/red/light_blue (default white) |
— | — |
daylight_sensor |
power source | inverted false/true (default false) |
— | — |
dispenser |
consumer | facing down/up/north/south/east/west (default north) |
— | — |
door |
consumer | facing north/south/east/west (default north); hinge left/right (default left); open false/true (default false); material wood/iron (default wood) |
below | output (0/1) |
dropper |
consumer | facing down/up/north/south/east/west (default north) |
— | — |
dust |
component | power 0..15 (default 0) |
below | output (0..15) |
glass |
transparent | — | — | — |
honey_block |
transparent | — | — | — |
hopper |
consumer | facing down/north/south/east/west (default down) |
— | — |
lamp |
consumer | lit false/true (default false) |
— | output (0/1) |
lever |
power source | mount floor/ceiling/north/south/east/west (default floor); on false/true (default false) |
by mount | input |
note_block |
solid | — | — | output (0/1) |
observer |
component | facing down/up/north/south/east/west (default north); powered false/true (default false) |
— | — |
piston |
consumer | facing down/up/north/south/east/west (default up) |
— | output (0/1) |
pressure_plate |
power source | material stone/wood (default stone); on false/true (default false) |
below | input |
redstone_block |
power source | — | — | — |
repeater |
component | facing north/south/east/west (default north); delay 1/2/3/4 (default 1); powered false/true (default false) |
below | — |
slab |
transparent | half bottom/top (default bottom) |
— | — |
slime |
transparent | — | — | — |
stairs |
transparent | facing north/south/east/west (default north); half bottom/top (default bottom) |
— | — |
sticky_piston |
consumer | facing down/up/north/south/east/west (default up) |
— | output (0/1) |
stone |
solid | — | — | — |
target |
solid | — | — | — |
torch |
power source | mount floor/north/south/east/west (default floor); lit false/true (default true) |
by mount | — |
trapdoor |
consumer | facing north/south/east/west (default north); half bottom/top (default bottom); open false/true (default false); material wood/iron (default wood) |
— | output (0/1) |
wool |
solid | color 16 dye colours, e.g. white/red/light_blue (default white) |
— | — |
"Needs support" blocks must sit on a block that holds them (below), or on the block their mount names (E006).
Measured on Bedrock: full blocks (stone, wool, concrete, lamps, pistons, observers, ...) and glass hold on every
face; honey, hoppers and top-half slabs, stairs and trapdoors hold only on top; bottom-half slabs, stairs and closed
trapdoors, and daylight sensors, hold only underneath; open trapdoors hold nothing. Levers attach to any block
(Bedrock; Java differs, see below).
Inputs are what a test can drive (set); outputs are what it can read (expect), with the values they report.
dooroccupies two cells: put it in the lower cell and leave the cell above as air (.); the upper half is placed for you.woolandconcretetakecolor=(white, orange, magenta, light_blue, yellow, lime, pink, gray, light_gray, cyan, purple, blue, brown, green, red, black). Use colours to group sub-circuits.powered,lit,on,pressedandpowerdescribe the initial state; the game recomputes them.
Optional sections
ports:{ "inputs": [{ "name", "pos": [x,y,z] }], "outputs": [...] }. Names are 1 to 32 ofA-Z a-z 0-9 _ -, unique across inputs and outputs. Inputs sit on a lever, button or pressure_plate; outputs on a lamp, piston, sticky_piston, door, trapdoor, note_block, dust or comparator.tests: truth-table cases,{ "set": { input: 0|1 }, "afterTicks": n, "expect": { output: value } }.blocks: overrides applied after layers,[{ "pos": [x,y,z], "block": "<spec>" }], one per position.annotations:[{ "pos": [x,y,z], "text": "..." }], pinned to a block, shown in the viewer.steps: build steps,{ "title", "layers": [y, ...] }or{ "title", "positions": [[x,y,z], ...] }. Default: one step per layer.meta:name(1 to 80 printable characters),slug(optional;a-z0-9runs joined by-, at most 48, not a Windows device name such asconornul; derived from the name when omitted),description(at most 2,000),edition(bedrock, the default, orjava),gameVersion(e.g.1.26.50; Java26.3).
Tests: how they run
- Ticks are redstone ticks: 1 redstone tick = 2 game ticks = 0.1 s.
- Each case starts from the design as written, runs until stable (at most 64 ticks), applies
set, runsafterTicksmore ticks (at most 1,000), then readsexpect. Cases are independent. - Levers and pressure plates hold the value (
0off,1on). A button value of1is one press (stone 10 ticks, wood 15). - Lamps, pistons, doors, trapdoors and note blocks report
0or1. Dust and comparators report their power level,0to15. - Write
portsandtestsbefore placing blocks, from the brief. rsd validateruns the tests in an approximate simulator (stage 3). T001: a case failed; the message lists expected and actual per output. T002:afterTicksabove 1,000, or the design is too big to simulate that long. T003 (info): the case was skipped because it needs something not modelled (a comparator reading a container, a daylight sensor, a piston moving a container or a slime or honey block); check it in game.- Bedrock delays the simulator uses (Java: below): torch, comparator, lamp, door and piston 1 tick; repeater 1 tick per delay step;
observer reacts after 1.5 ticks and pulses for 1 (inferred from the observer clock period measured in game). Leave a tick or two of margin in
afterTicks. If a case passes in game but fails here, the game is right: report it. - The viewer's Power menu shows any test case tick by tick: dust brightness is its level.
Bedrock rules that bite
- No quasi-connectivity. A piston is powered only by power at the piston itself: an adjacent powered block (above it too, unless it faces up), dust pointing into it, a torch beside it, a repeater facing into it. Power two blocks above it, or diagonally above, does nothing (that is Java's quasi-connectivity). Each piston in a stack needs its own power.
- Dust loses one level per block; after 15 blocks it is dead. Insert a repeater.
- A torch turns off when the block it hangs on is powered, and strongly powers the block above it.
- A redstone block powers components and dust next to it, not solid blocks.
- Patterns from Java tutorials often fail on Bedrock. If unsure, keep the circuit simple and test it.
Java Edition
Set "edition": "java" (and "gameVersion": "26.3"). The format, orientation and block list are the same;
the rules differ:
- Quasi-connectivity works: a piston also fires from power at the block above it, but only when it gets a block update. Prefer direct power; the simulator fires it at once, the game may not.
- Measured delays, in game ticks (one
afterTickstick is 2): torch, comparator 2; repeater 2 per step; observer 2, pulse 2; piston 3; lamps light at once and go dark after 4; doors at once. - Supports: levers need a solid face, like buttons; honey holds nothing; a hopper's top holds dust, repeaters and plates, not torches, levers, buttons or doors. Slime conducts.
- Export with
rsd export <file> --to schem -o <dir>(WorldEdit://schem load,//paste) or--to litematic(Litematica). There is no.mcfunctionor.mcpackfor Java.
Limits
2 MB file; JSON nesting depth 8; bounding box 64 × 384 × 64 (E007); 90 legend entries; 50,000 non-air blocks;
10,000 blocks overrides; 64 inputs and 64 outputs; 64 tests; 500 annotations of at most 500 characters; 384
steps. Exceeding a limit is E013.
The loop
- Brief: restate the job as named inputs, outputs and a truth table; write
portsandtestsfirst. - Draft: write
layersandlegend. Spread the circuit out; compact layouts come later. - Check:
rsd validate design.rsd.json --json. It prints{ "ok": bool, "diagnostics": [...] }and exits 0 (ok, warnings allowed), 1 (errors) or 2 (usage or file problem). Through MCP (rsd mcp), thevalidatetool returns the same JSON andsimulateshows each test's actual outputs. - Fix: each diagnostic has a
code, apos[x,y,z](or a JSON Pointerpath), amessageand ahintnaming the edit. Apply it and go to 3. Stop after 6 rounds and report what still fails. Never edit ports or tests to make a failure go away; if the brief was wrong, say so. - Show:
rsd bundle design.rsd.json -o design.htmlwrites one self-contained HTML viewer (publish it as an artifact or open it offline). With no tools at all, write a share link (see "Share link by hand").rsd sheets design.rsd.json -o sheets/writes printable A4 layer SVGs. - Export:
rsd export design.rsd.json --to mcfunction --pack <pack-dir> [--clear]writesfunctions/rsd/<slug>.mcfunction. In game, stand at the build origin and run/function rsd/<slug>; the build lands one block east.--clearalso writesrsd/<slug>_clear, which empties the bounding box. Run the clear function before rebuilding in the same place: building over a build pushes moving parts again, and leaves dust that is powered at rest at signal 0 until something changes.--to mcstructurewritesstructures/rsd/<slug>.mcstructureinstead; in game,/structure load rsd:<slug> ~1 ~ ~(add90_degreesand so on to turn it). Use it for builds over 10,000 blocks (E014).rsd pack design.rsd.json -o <slug>.mcpackzips both into a one-design behavior pack: open it on any Bedrock device to import, enable it on a world with cheats on, then use/functionor/structure loadas above. It holds both files, so it is refused (E014) above 10,000 commands; use--to mcstructurefor those.
Common codes: E002 ragged grid, E003 character not in legend, E004 unknown type (hint has the closest names),
E005 bad key or value (hint lists the allowed ones), E006 a component with nothing that holds it, E008 position
outside the grid or on air, E011 port or test reference, E012 slug, E013 limit. Stage 2 warnings point at likely
wiring mistakes: W101 dust too long, W102 a repeater or comparator pointing at nothing, W103 dust that
connects nothing, W104 a torch that powers its own support (a burnout loop), W105 a piston that only Java's
quasi-connectivity would power. W108 means a block's in-game mapping is not verified yet; it is safe to ignore
while authoring. --strict makes any warning fail. The full list is in docs/diagnostics.md, and the --json
output follows packages/core/schema/diagnostics.json.
Common mistakes
- Rows of different lengths, or a layer with a missing row (E002). Count characters.
- Reading layers top-down.
layers[0]is the bottom. - Mixing up
mountandfacing: torches, levers and buttons usemount(where the support is). - A component with nothing under it: dust, repeaters, comparators, pressure plates and floor torches need a block below that holds them; a bottom slab does not (E006).
- A port on air, or an output port on stone (E008, E011).
- Relying on quasi-connectivity for a piston (see above).
Worked example: repeater line
A lever drives 15 dust; a repeater refreshes the signal for a lamp further on.
{
"format": "rsd/1",
"meta": { "name": "Repeater line", "edition": "bedrock" },
"legend": {
"#": "stone",
"L": "lever mount=floor",
"-": "dust",
">": "repeater facing=east delay=2",
"*": "lamp"
},
"layers": [["###################"], ["L--------------->-*"]],
"ports": {
"inputs": [{ "name": "A", "pos": [0, 1, 0] }],
"outputs": [{ "name": "out", "pos": [18, 1, 0] }]
},
"tests": [
{ "set": { "A": 1 }, "afterTicks": 6, "expect": { "out": 1 } },
{ "set": { "A": 0 }, "afterTicks": 6, "expect": { "out": 0 } }
]
}
Layer 0 is a stone floor; layer 1 holds the circuit. The lever is [0,1,0], the repeater [16,1,0] facing east
(the direction the signal leaves), and the lamp [18,1,0].
Worked example: NOT gate
The lever's dust points into a stone block; the torch on that block's east face goes out when the block is powered, and the lamp beside the torch follows it.
{
"format": "rsd/1",
"meta": { "name": "NOT gate" },
"legend": { "#": "stone", "L": "lever mount=floor", "-": "dust", "t": "torch mount=west", "*": "lamp" },
"layers": [["#####"], ["L-#t*"]],
"ports": {
"inputs": [{ "name": "A", "pos": [0, 1, 0] }],
"outputs": [{ "name": "out", "pos": [4, 1, 0] }]
},
"tests": [
{ "set": { "A": 0 }, "afterTicks": 4, "expect": { "out": 1 } },
{ "set": { "A": 1 }, "afterTicks": 4, "expect": { "out": 0 } }
]
}
mount=west names where the torch's support is: the stone at [2,1,0], west of the torch at [3,1,0].
Share link by hand
To show a design without any tools: write it as one line of minified JSON, then replace every % with %25,
every space with %20 and every # with %23 (nothing else needs changing), and put the result after
https://timrross.github.io/dustwright/view/#json= (a local build serves the same /view/). The site
accepts at most 64 KB of fragment as the browser sends it, and browsers encode ", < and > again (%22, %3C,
%3E), so keep the JSON under about 20 KB. For the NOT gate it starts
https://timrross.github.io/dustwright/view/#json={"format":"rsd/1","meta":{"name":"NOT%20gate"},"legend":{"%23":"stone",...
Patterns
Known-good designs, each built and tested in game, in patterns/ (MCP: list_patterns, get_pattern):
not-gate, or-gate, and-gate, xor-gate, rs-latch, repeater-line, pulse-extender (button held about
2.6 s), pulse-limiter (one 4-tick pulse), observer-clock, repeater-clock (button starts it), t-flip-flop,
piston-door-2x2, hopper-sorter (load its filter hopper as its notes say).
Stamp them instead of copying: "include": [{ "pattern": "not-gate", "at": [x,y,z], "rotate": 90 }] puts the
pattern's lowest corner at at, turned clockwise seen from above (0, 90, 180, 270). Leave those cells as air in
the layers; write your own ports and tests at the stamped positions. A legend may then be empty.