The PRISM logo: a triangular prism refracting a beam into red, green, and blue

PRISM

One image in, multiple representations out.

PRISM is an image format built from a written spec outward: one container holding either a lossless raster payload with format-defined scaling, or a compact binary vector payload, with an optional authenticated encryption wrapper. The reference implementation is a dependency-free Rust library, and every byte of the format is documented in normative specs short enough to read in a sitting.

Every viewer scales it the same way

No raster format today defines its own scaling math, so the same PNG looks different in every application that resizes it. PRISM mandates the reconstruction kernel (Catmull-Rom, specified in pure integer fixed-point arithmetic), which makes scaled rendering bit-identical across platforms by construction. Scaling to native size returns the stored pixels exactly, so the lossless guarantee and the scaling guarantee are the same math.

Side by side comparison of smooth color fields at 8x magnification: nearest-neighbor on the left shows visible square blocks, PRISM reconstruction on the right is perfectly smooth
Smooth content is where reconstruction earns its keep: a 4,469-byte PRISM file at 8x. Left: nearest-neighbor, visibly built from squares. Right: the mandated reconstruction, and every conforming viewer produces these exact bytes.
Side by side comparison of a graphic with hard edges at 8x: both sides show stepped edges, the right side renders the background gradient smoothly
Honesty at the edges: on hard-edged graphics (913-byte file, 8x), reconstruction smooths the gradient but cannot invent edge detail that was never sampled; the steps stay steps. Content like this belongs in the vector payload below, where edges are math instead of samples.

True vectors in the same containerLIVE

Geometric content does not need pixels at all. The vector payload stores pooled styles and paths with delta-encoded fixed-point coordinates, in the tradition of Haiku's HVIF and TinyVG. The canvas below is not a picture of the logo: it is a 214-byte .prism file, fetched over the network and decoded by your browser right now, running prism-core compiled to WebAssembly. Drag the slider; every frame is a fresh rasterization from those same 214 bytes.

480 × 480
Loading the WebAssembly decoder…
assets/logo.prism, 214 bytes on disk, rasterized at whatever size the slider asks for. No JavaScript drawing code and no image asset beyond the 214 bytes and the decoder itself.

Compression that earns its bytes

The raster codec is QOI's byte-aligned op vocabulary re-based on JPEG-LS median edge prediction, so a run means "the predictor kept being right," which follows smooth gradients instead of only flat regions. Tiles make decode partial and parallel. On the synthetic corpus, against the raster incumbents and, for honesty, SVG:

imagerawPRISMQOIPNGSVG
gradient1,048,576131,923263,698148,692631 a
plasma1,048,576241,085281,766298,299397,866 b
shapes1,048,5766,8407,16110,584219 a
noise1,048,5761,048,1481,048,0811,049,2361,399,118 b
totals4,194,30434.0%38.2%35.9%42.9%

a: hand-authored geometry (shapes.svg, gradient.svg), visually equivalent but not pixel-exact; SVG's gradient interpolation and anti-aliasing differ at the pixel level, and PRISM, QOI, and PNG are all bit-exact. b: SVG cannot express sampled pixels, so the honest measurement is the source PNG embedded in the SVG as base64, a 33% markup.

The gradient row is the raster thesis in one number: prediction-runs land at half QOI's size. Noise compresses for nobody, as the pigeonhole principle requires; honest formats stay at 100% there rather than pretending otherwise. And the SVG column is the whole project's thesis in miniature: when an image truly is geometry, no raster codec comes within 30x of a scene description, and when it is samples, SVG has to smuggle a raster codec inside itself and pay base64 rent. The right response to that column is a container holding both representations, which is what PRISM is.

34.0%of raw RGBA on the corpus, vs QOI 38.2% and PNG 35.9%
214 Bvector logo, crisp at every size
0dependencies in prism-core by default
29tests: round-trips, hostile inputs, tamper detection

From PNG to .prism, bit for bit

Conversion is one command, and fidelity is not a claim, it is a test: prism encode then prism decode returns the exact source pixels, and the benchmark harness fails the run on any single-byte mismatch. Converting real PNG files, not raw pixels:

sourcePNG on disk.prismof PNG sizedecode
shapes, 512 x 51210,5846,84064.6%bit-exact
plasma, 512 x 512298,299241,08580.8%bit-exact
gradient, 512 x 512148,692131,92388.7%bit-exact
logo render, 768 x 76880,91072,79290.0%bit-exact
demo graphic, 48 x 482,72791333.5%bit-exact

The last two rows tell the deeper story. That 768px logo render costs 72,792 bytes as pixels, faithfully kept; the same image as a PRISM vector payload costs 214 bytes and scales forever. Choosing the right representation beats any codec, and PRISM is one container that holds both.

A real photographLIVE

Everything above used synthetic test patterns. Here is an actual camera photograph: The Blue Marble, shot by the Apollo 17 crew on December 7, 1972, NASA public domain. Real optical grain, real cloud texture, real JPEG-source noise, nothing hand-authored. The image below is fetched and decoded live, the same WebAssembly decoder as the logo above, just handed a raster payload instead of a vector one.

Loading the WebAssembly decoder…
assets/photo.prism, decoded live over the network and painted to this canvas with no PNG or JPEG involved after the initial camera capture.
encodingbytesof raw RGBA
raw RGBA6,558,720100%
PNG (lossless)3,553,50054.2%
QOI (lossless)2,729,95241.6%
.prism (lossless)2,533,77638.6%

PRISM lands at 71.3% of PNG's size and 92.8% of QOI's, on a genuine photograph, not a synthetic where the codec's own assumptions get to pick the test. A pixel-level check across the decoded file confirms zero differences from the source: every one of the roughly 1.6 million pixels came back exactly, alpha channel included. Cropped 3x into the cloud detail with no smoothing, drawn straight out of the canvas above:

A live crop at 3x, nearest-neighbor with no smoothing so nothing hides a mismatch. This is what "lossless" is a promise about: not that it looks fine, that it is identical.

For scale: the original JPEG this photo shipped as is 431 KB, far smaller than any lossless encoding here. That is not a fair fight; JPEG is a lossy codec that deliberately discards data the eye will not miss, a different category of promise than PRISM's raster payload makes today. Closing that gap is lossy mode, and it is not in version 1.

Against SVG

The fair fight for the vector payload is SVG, so here it is. The same logo, authored twice: once as hand-minified SVG, once as PRISM vector records. Both render below; the SVG is drawn by your browser, the PRISM version by our integer-only rasterizer.

The prism logo rendered by the browser from SVG
SVG: 542 bytes minified, 316 gzipped.
The prism logo rendered by the PRISM reference rasterizer
PRISM vector: 214 bytes, no compression step needed.

PRISM lands at 39% of the raw SVG and 68% of the gzipped SVG, before anyone optimizes the encoder, because binary opcodes, delta coordinates, and varints do structurally what gzip recovers only statistically. The honest other side: SVG opens everywhere and PRISM opens in PRISM; ubiquity is the one feature no format design can encode. On bytes, the vector payload is exactly where it should be: ahead of the incumbent.

The rest of the container

Spec-driven, and a teaching project

Every phase wrote its normative spec before its code: container, raster payload, reconstruction, vector payload. The implementation doubles as a Rust course that assumes no image processing background: a plain-English intro, a glossary of every term of art, and six lessons tying each phase to the language features it exercises, from wrapping arithmetic and slices to fixed-point determinism and cargo features.

Source, specs, and research notes on GitHub.