Vmprint
What if your documents could react to their own layout? VMPrint is a patent-pending spatialtemporal layout engine. It's tiny, fast and pure JavaScript — It runs on anything, be it the edge, a mobile phone, or embedded entirely in a static HTML page. Desktop publishing power without a headless browser in sight.
Install / Use
/learn @cosmiciron/VmprintREADME
VMPrint
VMPrint now ships with a browser preview demo.
Render real VMPrint documents in-browser with multilingual layout, pagination, and high-fidelity canvas preview.
The demo is a static, self-contained page you can open directly from a local folder, and its four core runtime bundles (shared fontkit + engine + web font manager + canvas context) weigh about 0.89 MiB total minified.
Open the canvas demo | Browse all examples | Quickstart
You've been here before.
The HTML/CSS pipeline got you 80% of the way. Then the tables started fighting the page breaks. You needed the header to say "Page 1 of 12" — but you can't know it's 12 until you've finished laying out the whole document, and by then it's too late. You tried a second pass. The second pass introduced new problems. Someone suggested spinning up a headless Chromium. It worked, mostly, except on the edge runtime, and the Lambda with the tight memory limit, and that one machine where the fonts came out wrong.
The stack stopped making sense. You kept patching.
VMPrint exists for that moment. Here is what it actually produces:
325 pages. 80,000 words. Markdown to publication-standard PDF. 2.32 seconds. Surface Pro 11 tablet, running on battery. No browser. No second pass. No auxiliary files.
Not because VMPrint is faster at doing what those tools do. Because it does something different.

Every element arrived through collision — geometry negotiated in a deterministic sweep. Each block, glyph, and inline span is an actor reporting its own origin and extent. All images in this README — including annotations, measurement guides, overlays, and script direction markers — are rendered entirely by VMPrint.
A Different Kind of Engine
VMPrint is a deterministic spatial simulation engine. Pages are bounded arenas. Document elements are autonomous actors with geometries. Layout is the process of reaching a stable world state.
There is no DOM underneath. No browser. No HTML. No CSS box model. The engine doesn't know what a browser is. It knows what a constraint field is. It knows what a collision is. It knows what a snapshot is, what a rollback is, and what it means for a world to settle.
This isn't a metaphor. It is the literal architecture.
A drop cap isn't a character. It's an actor with rules about how nearby text must respond. A table isn't a grid. It's a formation that holds together across boundaries, splits under constraint, and reconstructs itself on the other side. Derived regions are runtime participants too — observing, reacting, growing, and settling with the rest of the document world.
What this gives you is not just "better PDFs." It gives you a different substrate:
- a document runtime that behaves identically on a Cloudflare Worker, a laptop, a Lambda, and a phone
- a layout engine you can inspect, instrument, and participate in
- a pagination model that is not approximate — the same input produces the same output, always, everywhere
- a rendering output that is a flat, absolutely-positioned geometry list — no DOM, no layout re-traversal, just draw calls — and the right shape for GPU-accelerated rendering
- a foundation for tools the web made people forget were possible
Here is the part that makes the rest of this possible. When you hand the engine a document, each element — paragraph, table, heading, drop cap — gets compiled into a live actor. Think Lego Batman standing on the board. When Batman approaches a page boundary and doesn't fully fit, he doesn't get clipped and he doesn't get deferred. He disassembles into blocks. The blocks that fit commit to the current page. The remaining blocks reconstitute on the next page as the same Batman — same identity, same memory, same relationships — picking up exactly where he left off. A table that spans three pages is one actor that has split and reformed twice. A drop cap is an actor that claimed its territory and pushed the surrounding text to respond. None of this is post-processing. It is just what actors do when they run out of space.
The output of all that activity is refreshingly simple: a flat list of absolutely-positioned boxes with exact coordinates and the story of how each one got there. No tree. No cascade. No layout to re-run at render time — just draw calls. That flatness is what makes rendering fast, output diffable, and the path to GPU acceleration a straight line.
And then there is this: because the document is a running simulation, the output doesn't have to capture equilibrium. The engine can be told to capture the world at a specific tick instead — which means each page can represent a successive world state. The document becomes a valid print artifact and a temporal record of a running simulation. Pages that evolve. A flipbook whose frames are world states, not hand-drawn images. That has no equivalent in any prior art layout system.

Actors by name, origin, size, font, and settled state. Five writing systems on one computed baseline. A naive engine drifts; this one does not.
Anti-Blackbox By Architecture
Most layout systems are black boxes. Not because the source is closed, but because the architecture gives you no meaningful point of entry. You hand it input. It gives you output. What happened in between is not your concern.
VMPrint was deliberately designed to be the opposite.

The same document rendered with
--debug. Every actor named. Every boundary measured. Every extent, margin, and origin labeled in place.
The actor contract means you can introduce your own participants into the same simulation as native ones. The communication bus means those participants can publish and observe signals inside the same world. The overlay system means you can draw what the engine sees. The snapshot and rollback model means layout bugs become reproducible facts instead of spooky behavior. And if reactive actors ever drive the engine into a geometric oscillation loop, it doesn't silently spin — it stops deterministically and surfaces a diagnostic identifying the oscillating actor, the triggering signal, and the exact frontier where the cycle was detected. The failure mode is observable and reproducible, not a hung process.

The same document, clean. What the reader sees. The machinery disappears. The precision does not.
The output is a flat array of absolute coordinates with semantic provenance attached:
{ sourceId: 'ast-node-10', fragmentIndex: 2, transformKind: 'split', isContinuation: true }
You can trace any pixel back to its source. You can diff layouts as JSON. You can replay a simulation deterministically. Transparency was designed in from the first commit.
Built-In Scripting, Not Bolted-On Templating
This is where the architecture starts to feel different from anything you've used before.
Most document pipelines can't let a document respond to its own finished state. You don't know the total page count when you're authoring the footer. You don't know which headings survived layout when you're building the table of contents. You don't know what fits on page three until you've already committed to page three. So you guess, or you run two passes, or you inject values in post-processing. None of it feels clean because none of it is.
VMPrint includes a scripting layer that fires after the document has settled. The engine is the runtime. The document is a living world. Your script participates in it.
The document knows how many pages it is
---
methods:
onReady(): |
setContent("colophon", `Settled across ${doc.getPageCount()} pages.`)
---
onReady fires once — after layout has fully settled. By then, doc.getPageCount() returns the real answer. Not a guess. Not a pre-layout estimate. The actual settled page count, available to any element that wants it.
That is something template engines cannot do. That is something print CSS cannot do without a second pass. In VMPrint, it is two lines.
Elements can observe the document's own structure
---
methods:
onReady(): |
const chapters = elementsByType("h1")
sendMessage("toc", {
subject: "populate",
payload: {
pageCount: doc.getPageCount(),
chapterCount: chapters.length,
opening: chapters[0] ? chapters[0].content : "none"
}
})
toc_onMessage(from, msg): |
if (msg.subject !== "populate") return
const p = msg.payload
setContent(self,
`${p.chapterCount} chapters · ${p.pageCount} pages · Opens: "${p.opening}"`)
---
The toc element starts as a placeholder. After the document settles, the document itself queries its own heading structure, packages up real settled facts, and sends them as a message. The toc element receives the message and rewrites itself with content that could not have existed before layout.
This is the pattern for building tables of contents, document summaries, indices, statistics blocks — anything that needs to know what the document actually contains rather than what you hoped it would contain.
An element can replace itself — mid-document, without replay
---
methods:
onReady(): |
sendMessage("statusTable", { subject: "populate" })
statusTable_onMessage(from, msg): |
if (msg.subject !== "populate") return
replace([
{
type: "table",
name: "
