🍳 Foodlangalpha

Recipes as QR Codes

A Foodlang recipe is just text — which means it fits in a QR code. Compile a recipe to the qr target and you get a compact, self-identifying payload you can print on packaging, a menu, a shelf tag, or a machine's display. A machine scans it; if it speaks Foodlang, it parses the payload and runs the recipe — no app, no account, no network.

Try it on the Playground: pick a recipe, choose the qr target, and Compile — the scannable code renders right there.

QR codes are generated by qrpuppy.com.


The qr target

qr emits the recipe's own .food KDL, minified (indentation and blank lines stripped) so it stays small, with a leading // foodlang/1 marker so a scanner can recognize the format. Comments are valid strict KDL, so the whole payload still parses and executes unchanged.

foodlang compile drink.food --target qr
// foodlang/1
drink "Iced Vanilla Latte" {
ingredients {
coffee 18 g
vanilla_syrup 20 ml
milk 10 oz
ice 140 g
}
phases {
preinfuse coffee { pressure 2 bar; flow 2 mlps; time 6 s }
brew coffee to shot { pressure 9 bar; stop 40 g; max 28 s }
build cup { vanilla_syrup; shot; stir 5 s; ice; milk }
}
}

That payload is ~300 bytes and encodes to a QR around version 13 — small enough to print at a couple of centimetres and still scan reliably. A full recipe fits comfortably inside a single QR's byte budget.

Compliance: a minified KDL profile

.food is the cute authoring format, and every .food document is valid strict KDL — that is the higher, more abstract language it is checked against. The foodlang/1 QR payload keeps that guarantee: it is just .food with per-line whitespace and blank lines removed, which KDL (being insensitive to indentation) treats as identical. So the minified payload is itself valid strict KDL, and the leading // foodlang/1 marker is a KDL line comment — invisible to a parser.

The minification is also lossless: re-parsing a payload yields a recipe that recompiles to byte-for-byte the same canonical .food. Both properties are enforced for every example recipe in the test suite (tests/compile-qr.test.ts):

  • foodlang/1 payload → isStrictKdl()valid, for every example.
  • foodlang/1 payload → parse → compile(kdl)equals the original.

In short: .food ⊂ KDL and foodlang/1 ⊂ .food ⊂ KDL. A scanner that only knows KDL can still parse the code; one that knows Foodlang can run it.

Fidelity executable — a Foodlang-speaking machine runs it directly
Category Distribution & QR
Marker // foodlang/1 on the first line
Body minified strict-KDL .food, byte-for-byte re-parseable

Going smaller: the compressed foodlang/1z variant

The readable payload above is already the smallest a recipe gets while staying valid KDL text — there is no separate "minified KDL" dialect (KDL's only compaction is the ; node terminator, which is byte-for-byte the same as a newline). To go smaller you have to give up KDL-as-text, so it's opt-in.

The qrz target DEFLATE-compresses the readable payload. The QR then carries the raw compressed bytes with a short foodlang/1z marker — no base64 tax — and a scanner inflates it back to the exact // foodlang/1 KDL before running it.

foodlang compile examples/crunchy-roll.food --target qrz
# foodlang/1z:<base64url>   (the copy-pasteable text token)
recipe readable foodlang/1 compressed QR bytes
iced-vanilla-latte 298 B 214 B (−28%)
crunchy-roll 390 B 240 B (−38%)

The saving grows with recipe size. It is still lossless: both the ASCII foodlang/1z: token and the raw-byte QR inflate to byte-for-byte the same readable KDL (enforced for every example in tests/compile-qr.test.ts). The trade-off: the compressed QR no longer contains human-readable KDL — only a Foodlang-aware scanner can read it, whereas the default foodlang/1 degrades gracefully to plain text. Use qr by default; reach for qrz on tiny labels or long recipes.

In the Playground, pick the qr or qrz target to see each rendered as a live QR with its byte count.


How a machine executes a scanned recipe

┌───────────┐   scan    ┌───────────────┐   parse   ┌──────────────┐
│  QR code  │ ────────▶ │ // foodlang/1 │ ────────▶ │  Foodlang    │
│ (printed) │           │  recipe text  │           │  interpreter │
└───────────┘           └───────────────┘           └──────┬───────┘
                                                            │ compile to the
                                                            │ machine's native
                                                            ▼ control target
                                                     ┌──────────────┐
                                                     │  espresso /  │
                                                     │  PLC / robot │  ▶ runs
                                                     └──────────────┘
  1. Scan. The camera reads the QR and recovers the text payload.
  2. Recognize. The // foodlang/1 marker tells the device this is a Foodlang recipe, not a URL or plain text.
  3. Parse. The device parses the strict-KDL body into the recipe AST.
  4. Execute. The device compiles the AST to whatever it speaks natively — a flow-profiling espresso profile, an S88 phase sequence, an OPC UA program, a robot plan — and runs it. These are the same compiler targets the CLI and playground expose.

A device that doesn't speak Foodlang still gets human-readable text, so the code degrades gracefully: worst case, a person reads the recipe.


Ordering vs. recipes

Two different QR flows share the same idea — put Foodlang in a QR:

  • Recipe QR (this page, the qr target): the whole executable recipe. Scanned by a machine to make the item.
  • Order QR (the Order page, see the Order Format & Tokens spec): just item ids + the customer's variant choices, assuming each id already has its own recipe on the kitchen's side. Scanned by a clerk to take the order.

Both are generated client-side and both are rendered by qrpuppy.com.


Related exports

The qr target sits alongside the other distribution-minded exports:

  • BevFacts (bevfacts target) — an FDA-style Nutrition Facts label estimated from the drink's ingredients, for the printed side of the package the QR rides on.
  • Schema.org Recipe, Cooklang, ORF — human/web publishing formats.

See the full list on the Targets page.