Skip to content

HTML ⇄ PPTX · lossless by construction

Edit a PowerPoint deck
as a web page.

And get the deck back: not an approximation of it. pptx-html reads a .pptx into a slide model, renders it as HTML you can edit, reads the edit back and writes the deck out again. Four legs, one loop, no screenshots anywhere.

What it is

Four functions, arranged in a circle

pptx-html moves slides between HTML and PPTX by driving @shbernal/ts-pptx. It reads a .pptx into a slide model, renders that model as HTML, reads the edited HTML back, and writes a .pptx out again, so a deck can be edited by anything that can edit a web page, and still be a deck afterwards.

.pptx  ──import──►  IR  ──render──►  HTML   (what a human sees / edits)
  ▲                  ▲                 │
  └────emit──────────┴─────parse───────┘   (what a machine reads back)

Invariant R. For any deck this pipeline can write, import → render → parse → emit produces a deck equal under the normalized read model to the input. Slides whose features the IR does not model are carried across byte-identical rather than approximated.

Equality is normalized rather than byte-for-byte: zip entry order, timestamps, relationship ids and element ids all vary legally, and both sides are canonicalized before diffing. The property, and the harness that gates it →

The three states

Every construct is in one of three states, and never a fourth

Modeled

The intermediate representation represents it, and it survives the loop exactly.

Carried

The IR does not model it, so its XML moves across untouched. No approximation, and no loss.

Warned

It can be neither modeled nor carried, and the conversion says so. A visible failure, never a silent one.

Approximated

The state that does not exist here: content that comes out looking about right and has no way back. It is why the raster fallback was removed rather than kept as an escape hatch (a slide flattened into a picture can never re-enter the loop) and why this site shows no pictures of slides.

Those three states are a claim, and the fidelity ledger is its evidence: what the loop currently models, carries and warns on, per deck, measured from the same corpus the round-trip oracle gates on.

Scope

What the guarantee covers, and what it does not

A generated corpus of 26 decks runs the full loop on every CI build, and the per-construct fidelity ledger is snapshotted so it cannot move silently. Claims here are held to what that gate actually covers; where something is not covered, it says so.

  • Input domainDecks written by @shbernal/ts-pptx, which is what the generated corpus is made of, and what the two decks moving above are written by. Decks authored in PowerPoint are a deliberate second tier and are not yet gated.
  • EnvironmentThe loop is host-agnostic and runs in Node and the browser alike. The best-effort heuristic lane, for HTML this library did not render, is browser-only: it needs a real DOM.
  • DistributionPublished to npm as pptx-html. ESM only, Node >=24. Pre-1.0: the loop's four legs are stable, the heuristic lane's model is not.

Where to go next

Run it, or read why it is built this way

Released under the MIT License.