Recipe format
A recipe is the look with no image. It is a delta against the defaults, serialised to a short string.
The shape
recipe:v1:eyJ2IjoxLCJyZW5kZXJNb2RlIjoiZGl0aGVyIn0Shared as a link instead, the same payload rides as ?r= with no prefix:
https://www.ascii-magic.com/app?r=eyJ2IjoxLCJyZW5kZXJNb2RlIjoiZGl0aGVyIn0A typical recipe is 120 to 280 characters, because it only carries what you changed.
Decoding one by hand
- Strip the
recipe:v1:prefix. From a link, take therparameter instead. - Right-pad with
=until the length is a multiple of 4. The encoder strips padding. - Base64 decode.
- Parse as JSON.
printf '%s' "$payload" | base64 -d | python3 -m json.toolWhat comes back is the delta. Anything absent is at its default. It always carries
v: 1.
What is inside
Two parts. Top-level keys are engine settings that differ from their defaults:
renderMode, fontSize, charSet, coverage, the
backdrop keys, brightness, contrast, invert, plus a per-engine
block for whichever style you used, plus lights, colour, animation, dither and depth.
A pfx object carries post effects, and it is a per-property delta rather than a
whole effect block. So a recipe that only raised film grain carries
{"pfx":{"filmGrain":{"intensity":70}}} and nothing else.
{
"v": 1,
"renderMode": "halftone",
"fontSize": 14,
"htAngle": 45,
"pfx": { "filmGrain": { "enabled": true, "intensity": 70 } }
}Applying one
Applying is a full reset then layer, not a patch. Every key goes back to its default first, then the recipe is layered on top. So two recipes applied in sequence do not compound.
If a recipe names a Pro style and your account cannot render it, the style falls back to the free default and the rest of the recipe still applies. You get a toast saying so rather than a silent downgrade.
Recipes inside PNGs
Every PNG the app exports carries its own recipe in a tEXt chunk with the keyword
asciimagicRecipe. Other image tools ignore unrecognised chunks, so this is invisible
everywhere else.
To read one by hand, find that chunk and take everything after the NUL separator. It is a normal recipe code. Dropping such a PNG back into the editor loads the look automatically.
Two gotchas
Non-Latin-1 characters break encoding. The encoder uses btoa, so a text overlay
or a custom character set containing a character above U+00FF will throw. No shipped preset contains
one.
The base64 is not URL-safe. Padding is stripped but + and / are
not translated. In practice the payload is ASCII JSON so this is rare, but a custom character set
containing ?, > or ~ can produce a +, which a
query parser reads as a space. If a link misbehaves, paste the recipe:v1: code instead,
which is parsed with a regex and is unaffected.
A worked decode
Take the payload from a shared link and read it, with no tooling beyond a shell:
payload='eyJ2IjoxLCJyZW5kZXJNb2RlIjoiZGl0aGVyIn0'
# right-pad to a multiple of 4, the encoder strips padding
pad=$(( (4 - ${#payload} % 4) % 4 ))
printf '%s%s' "$payload" "$(printf '=%.0s' $(seq 1 $pad))" \
| base64 -d | python3 -m json.tool{
"v": 1,
"renderMode": "dither"
}That is a complete recipe. It says the mode is dither and nothing else differs from
the defaults, which is why it is so short. A recipe with a tuned look and a few effects runs 120 to
280 characters.
Why it is a delta
Encoding the full state would make every recipe roughly the same size, dominated by values nobody changed, and would freeze today's defaults into every link ever shared. A delta means a recipe carries intent rather than state: it says what you changed, and everything else follows whatever the engine currently considers normal.
The trade is that a recipe is interpreted against the defaults of the build that opens it. A
shipped default changing is therefore a real compatibility event, and v exists so that
a future format change can be detected rather than silently misread.
What a recipe does not carry
- Your image. A recipe is the look with no source. Opening one loads a demo so there is something to see it on.
- Anything at its default. If a key is absent, it is not "unset", it is normal.
- Export settings. Format and scale are decisions at export time, not part of the look.
- Your account or tier. Opening a recipe that uses a Pro style on a free account shows you the lock, not the render.
Through the MCP server
Two tools handle the format directly, which makes an agent able to read and write looks rather than only produce them:
| Tool | Direction | Use |
|---|---|---|
decode_recipe | code to settings | Read a shared look before changing it. Also the most reliable way to learn a mode's real parameter names, since a decode names every key it set. |
encode_recipe | settings to code | Hand a look back to the user as a link they can open in the editor. |
stylize_image with recipe | code to a render | Reproduce exactly. A recipe is never dressed, so this is the only call whose output is deterministic. |
Where recipes turn up
| Place | Form |
|---|---|
| The Recipes button in the editor | A recipe:v1: code to copy. |
| A share link | The same payload as ?r=, with no prefix. |
| An exported PNG | Written into a tEXt chunk, so the file remembers how it was made. |
| A project | The recipe plus the source it was made on. |
| MCP | Either tool above, or the recipe argument on a render. |
Last updated