Lua · Limits

The numbers, and why each one is where it is.

The chip has about 64 KB of usable heap, and a TLS handshake wants 18 KB of it contiguous. Every number below is chosen so that an app can be open and fetching at the same time. Exceed one and the app says which.

Source

24 KB

The largest single block the engine asks for, measured with the framebuffer evicted. Several times the size of the largest app written so far.

Live memory

48 KB

Enforced in the allocator, so an app that asks for more is stopped rather than allowed to take the OS down with it. The measured peak for a real game is 23–31 KB.

Fuel

200,000

VM instructions between checks, about 20 ms of solid compute. A runaway loop stops with an error instead of freezing the device.

Display list

96 entries, 3 KB

One frame's worth of calls and the text they carry. Ninety-six entries is more than fits on the panel at any text size.

Save blob

1 KB

One string per app, keyed on a hash of its filename. Enough for a bankroll, a high score, a cursor, or a short list; not enough to be a database.

Memory at lint time

70% / 85%

The linter fails an app whose measured peak crosses 70% of the 48 KB unpaired, or 85% with a 12,288-byte document staged. GitHub sat at 93% with nothing loaded, passed, shipped, and said “not enough memory” the first time it fetched a README.

Contiguity fails before capacity§

The number that decides whether an app runs is not how much heap is free. It is how large a block the allocator can still find in one piece. Lua asks for 1.5–2 KB at its largest; the bounded interpreter written to avoid it asked for 10,240 B, and that is the measurement that ended the argument.

The same concern runs through the rest of the OS. The extractor streams a response instead of holding it, so a 40 KB document becomes a small table without ever existing whole in RAM. Caches store extracted tables rather than documents. Each choice exists to keep an 18 KB contiguous block findable at the moment a handshake wants it.

What happens when you hit one§

Run past the fuel and the app stops with "this app ran too long without returning". Ask for more than the memory budget and it stops with "not enough memory". Both unwind to the engine, which shows the error and keeps the OS running.

The limit that is not enforced in software is the panel. A refresh takes about half a second. An app can move on its own — snail.tick(ms) exists and Snake crawls with it — but the interval is clamped to 600 ms, because a step nobody saw is a game running away from the person holding it. Turn-based is what the hardware is good at.

Next: One app, walked