TONER-1

State of the art among language models that run entirely inside a print job.

TONER-1 runs on hardware already in your copy room. It uses 0 host FLOPs, ships as a single file, and delivers every output fused to paper. It does not take prompts. It has a context window of 8.5 × 11 inches. It writes about printers.

Everything below is literal, except the section marked PARODY.

What it is

TONER-1 is Andrej Karpathy's llama2.c stories260K (260,032 parameters), fine-tuned by us on 8,757 short children's stories about printers, then exported to int8 with one scale per row, plus a forward pass written in PostScript. The whole model is one print job, TONER-1_print-job.ps (595,372 bytes of plain text): the weights, the 512-piece vocabulary, about 100 lines of inference code, and the page layout. Sent raw to a PostScript printer, it makes the printer's own CPU write a children's story of about 200 tokens, one token at a time, and then typeset it on a US Letter page. The computer that sends the file only sends bytes.

The float32 weights are here too, as model.safetensors, for use in Python.

It is a working print job and a parody of frontier-model launches. The stories are real, unedited model output. The benchmarks are not real evaluations.

Its bigger sibling, TONER-1 Pro, is our own 12.2M model with our own tokenizer.

Status

Printed on an HP Color LaserJet Pro MFP M281fdw on 2026-11-05: 200 tokens in 3:37 by the printer's own clock (0.92 tok/s), 3:39 from send to finished job.

A language model that ran on an actual printer: one print job, zero host FLOPs.

What the printer itself sent back over the network while the job ran: its own output and its own timer. These are the first and last lines; the story in between is left out here.

TONER-1 | 260032 params | int8 | PostScript-native inference
200 tokens in 217.235 s = 0.920662 tok/s [STEPS limit]

The combined TONER-1-V job printed the same day: the story in 3:38 and the doodle in 25 s by the printer's own clock, 4:04 from send to finished job. See TONER-1-V.

Full logs, with every byte the printer sent back and when it arrived: docs/printer-run-2026-11-05 in the GitHub repo.

Sample output

A TONER-1 story page: the story, every sampled token, and a run card (Ghostscript render)

A Ghostscript 10.07.1 render of samples/story_seed20_t0.8.pdf (seed 20, temperature 0.8), not a photo of a printed page. The page is the print job's own output: the story, all 200 sampled tokens, and a run card with the interpreter's name and its timing (82.3 tok/s). The story begins: "Once upon a time, there was a little girl named Lucy. She lived in a small house." A few sentences later: "Then, a little boy named Tim came to the printer."

How to run it

Preview in Ghostscript (no printer needed)

gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSEED=7 -dTEMP=0.9 -o story.pdf TONER-1_print-job.ps

This takes a few seconds on an Apple M4 Mac and writes a one-page PDF. The story text and the timing also go to stdout. Optional parameters:

Name Default Meaning
SEED 42 seed for PostScript's rand
TEMP 0.8 sampling temperature; 0 is greedy
STEPS 200 maximum number of tokens

Print it

Only on a PostScript printer you own or have permission to use. Send the file raw, so that the printer's PostScript interpreter runs it instead of a driver printing it as text:

nc <printer-ip> 9100 < TONER-1_print-job.ps        # raw TCP port 9100
lpr -P <queue> -o raw TONER-1_print-job.ps         # or through a raw CUPS queue

A printer uses the defaults (seed 42, temperature 0.8, 200 tokens). To change the seed, edit the 42 in /SEED 42 def, which appears once in the file.

Before you send it:

  • It needs a PostScript interpreter, Level 2 or later. The job uses only Level 1 operators plus rectstroke (Level 2), and the standard Helvetica, Times and Courier fonts. A PCL- or GDI-only printer may print the 595 KB file as pages of text.
  • It is slow. On an HP Color LaserJet Pro MFP M281fdw it wrote 200 tokens in 3:37 by the printer's own clock (0.92 tok/s), and took 3:39 from send to finished job. No paper comes out until the story is finished. A long job can hit a printer's job timeout or run out of PostScript memory (VM). Ghostscript uses about 3.3 MB of VM writing a PDF and 7.5 MB rendering to PNG.
  • PostScript's rand is implementation-defined, so another interpreter may tell a different story from the same seed. At seed 42, the HP's story was byte-identical to Ghostscript's, apart from the printer's CRLF line endings.

Use the weights in Python

model.safetensors holds the fine-tuned float32 weights, before int8 quantization, and config.json holds the architecture. They load into llama2.c's model.py. The vocabulary is Karpathy's tok512.model, from karpathy/tinyllamas:

import json, torch
from safetensors.torch import load_file
import sentencepiece as spm
from model import ModelArgs, Transformer      # model.py from github.com/karpathy/llama2.c

cfg = json.load(open("config.json"))
m = Transformer(ModelArgs(**cfg["model_args"]))
missing, unexpected = m.load_state_dict(load_file("model.safetensors"), strict=False)
assert missing == ["output.weight"] and not unexpected   # output.weight is tied to tok_embeddings.weight
m.eval()

with torch.no_grad():
    out = m.generate(torch.tensor([[1]]), max_new_tokens=200, temperature=0.8)[0, 1:].tolist()  # 1 = BOS
ids = out[:next((i for i, t in enumerate(out) if t in (1, 2)), len(out))]   # stop at BOS or EOS
print(spm.SentencePieceProcessor(model_file="tok512.model").decode(ids))

This samples with PyTorch's random numbers, not PostScript's, so it tells different stories from the print job.

Rebuild the job from its parts

python3 src/l2c_safetensors.py to-bin . toner1.bin              # model.safetensors -> llama2.c .bin
python3 src/export.py toner1.bin tok512.bin src/weights.ps      # regenerates src/weights.ps
sh src/build.sh > rebuilt.ps && cmp rebuilt.ps TONER-1_print-job.ps   # byte-identical

tok512.bin is in karpathy/tinyllamas. These commands reproduce the shipped files byte for byte. l2c_safetensors.py uses only the Python standard library.

Specs

Base model stories260K from Karpathy's llama2.c (published in tinyllamas), trained by him on TinyStories
Fine-tune ours: 3,000 steps (about 11 minutes on an Apple M4), lr 3e-4, on 8,757 printer stories mixed with TinyStories replay (TONER-1-printer-stories)
Parameters 260,032
Architecture Llama-2-style decoder: 5 layers, dim 64, 8 query heads sharing 4 key/value heads, head size 8, SwiGLU feed-forward (hidden 172), RoPE, RMSNorm; the embedding is shared with the output layer
Vocabulary 512-piece SentencePiece (tok512), Karpathy's, unchanged
Context 512 tokens; the job generates up to 200
Quantization int8 weights, one scale per row (scale = max abs weight / 127); activations are PostScript reals
Input None. Every story starts from the beginning-of-sequence token.
Output One US Letter page: the story, the token stream, and a run card
Print job 595,372 bytes, 455 lines of plain text, one page
PostScript Level 1 operators plus rectstroke: needs Level 2 or later

Did the fine-tune work?

Sampled from the int8 weights the way the print job samples (from BOS, temperature 0.8, up to 200 tokens):

Original stories260K TONER-1
Stories with any printer word (n = 300) 3.7% 87.3%
Stories that say "printer" (n = 300) 0% 71.7%
Loss on 461 held-out printer stories 1.83 1.23
Loss on TinyStories validation 1.341 1.393

Blind story test. One AI rater scored 50 shuffled Ghostscript transcripts blind, on 1-to-5 scales. The original scored 1.90 coherence, 3.65 language and 1.90 fun; TONER-1 scored 1.70, 3.30 and 2.25, and 9 of its 20 stories were printer-themed (0 of 20 for the original). So the fine-tune trades a little coherence (not significant, p = 0.37) for printer content and fun (p = 0.04). This is one AI rater on 40 stories from these two models, not a human study.

Does the PostScript compute the right thing? Teacher-forced on 96 tokens of a printer story and of a TinyStories story, the logits in Ghostscript are within 7.0e-5 of an int8 float64 reference, and the most likely token agrees at all 96 positions.

Speed

Where Speed Status
Ghostscript 10.07.1, Apple M4 Mac about 80 tok/s, under 4 s per page (the sample page logged 82.3) measured
HP Color LaserJet Pro MFP M281fdw, 2026-11-05 0.92 tok/s: 200 tokens in 3:37 by the printer's own clock; 3:39 from send to finished job measured, one run (seed 42)

Before that run, the printer speed was an estimate, 0.3 to 5 tok/s; 0.92 falls inside it. The printer's logs are in the GitHub repo under docs/printer-run-2026-11-05/.

Files

File What it is
TONER-1_print-job.ps The model: one print job (595,372 bytes)
model.safetensors The fine-tuned float32 weights (llama2.c model.py names; output.weight is not stored, it is tok_embeddings.weight)
config.json Architecture and token ids
src/engine2.ps The forward pass, sampler and page layout, in PostScript
src/weights.ps The int8 weights and vocabulary as written by export.py. The print job already embeds them.
src/export.py Converts a llama2.c .bin and tok512.bin into weights.ps
src/l2c_safetensors.py Converts between llama2.c .bin and model.safetensors (standard library only)
src/build.sh DSC header + weights.ps + engine2.ps = the print job
docs/TONER-1_model-card-and-benchmarks.pdf The two-page model card and parody benchmarks, as a PDF. The handout is itself a PostScript program.
docs/TONER-1_explainer.pdf A two-page technical explainer: how a print job runs a language model
samples/story_seed20_t0.8.pdf One story page, produced by the print job in Ghostscript
samples/story_seed20_t0.8_ghostscript-render.png A 110 dpi Ghostscript render of that page

Limitations

  • It has 260K parameters. The stories are short and simple, and often stop making sense ("She loved to bring paper to a avocado"). It mostly writes about Lily, Tim and the printer.
  • There is no prompt input. The model chooses the plot.
  • English only, in the style of TinyStories.
  • Printer speed comes from one run on one printer. Other printers have not been tried.
  • PostScript's rand is implementation-defined, so seeds are not guaranteed to be portable between interpreters (the HP matched Ghostscript at seed 42).
  • PCL- and GDI-only printers cannot run it. Long jobs can hit a printer's job timeout or VM limit.
  • Outputs are fused to paper and cannot be edited.

Benchmarks (PARODY)

PARODY. Not real evaluations. Frontier models from major labs were not run on these benchmarks and are not named. "N/A" means only that they cannot execute inside a print job.

Benchmark (parody) What it pretends to measure TONER-1 Frontier models from major labs
TPS (Tokens Per Sheet), higher is better Output tokens per US Letter sheet, simplex. Not tokens per second. 200 N/A: cannot execute inside a print job
HelloPaperJam, higher is better Copy-room commonsense, 4-way multiple choice (chance 25%); parody of HellaSwag 27.9% N/A: cannot execute inside a print job
PC LOAD LETTER, higher is better "Truthfulness": share of outputs printed on the paper size the job asked for; parody of TruthfulQA 100.0% N/A: cannot execute inside a print job
Jam Rate, lower is better "Hallucination": invented objects per 100 tokens 1.5 N/A: cannot execute inside a print job
TonerBench, lower is better Dollars per 1M output tokens, in toner: 5,000 sheets of 200 tokens at about 2.5 cents each. Paper extra. $125 N/A: cannot execute inside a print job

The charts are on page 2 of docs/TONER-1_model-card-and-benchmarks.pdf.

Prior art

Credits

  • Base model, tokenizer, architecture and inference reference: Andrej Karpathy's llama2.c and tinyllamas (stories260K, tok512), MIT License.
  • TinyStories, by Ronen Eldan and Yuanzhi Li (2023), arXiv:2305.07759, CDLA-Sharing-1.0: the base model's training data, and replay data in the fine-tune.
  • The printer stories: written by Xing4.0-29B-A4B (China Telecom's TeleAI, Apache-2.0) through OpenRouter's free tier, and filtered by us. They are published as TONER-1-printer-stories (Apache-2.0).

License

MIT; see LICENSE. The weights are our fine-tune of Andrej Karpathy's stories260K (MIT). No Llama 2 materials are in this repository.

Links


Not affiliated with or endorsed by Adobe, HP, Artifex, Google, or any AI lab.

We're proudly pushing forward the PostScript Pareto Frontier.

Downloads last month
333
Safetensors
Model size
260k params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for jacobalansussman/TONER-1

Finetuned
(7)
this model

Datasets used to train jacobalansussman/TONER-1

Paper for jacobalansussman/TONER-1