Skip to content

Latest commit

 

History

253 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

robby — the reverse-obverse image compiler

What if a digital image could be a two-sided image-object, like a postcard or a coin?

robby is a tiny, explainable compiler that turns one photograph—the obverse—into a deterministic companion reverse. Together they form a two-sided image-object. Like a coin or a postcard, the two faces are never shown at once: you flip between them. The reverse is evidence of a reproducible computation, never proof of ownership, authorship, or truth.

Names in the project

  • troid — the compiler engine. The only thing this project ships as a downloadable binary.
  • robby — the observation deck: the web frontend that hosts the gallery, live source editor, compilation trace, and explainability panels. A website, not an install.
  • .robby — the recipe language: the source program fed to troid. Untouched by this repo's naming pass.
  • robby-compiler — the Rust crate that holds the library and the IR / manifest contract. Kept verbatim so existing IR records (robby-ir-v1, robby-render-manifest-v1, robby-binding-record-v1) remain readable.

Built for SegFault 2026, a compiler-technology hackathon, under the Explainable compilers challenge — "modern compilers are enormous black boxes; make their decisions legible to the people using them."


What problem is this solving?

Most image tools hide their reasoning. You get a filtered photo, a stylized export, an AI-generated variant — and no visibility into why it looks the way it does. robby takes the opposite position: every pixel in the reverse can be traced back to a specific, inspectable, repeatable step in a compiled script.

This isn't a photo editor, compositor, or masking tool. It's a compiler — with a source language, a parser, an intermediate representation, and a deterministic execution step — that happens to output images instead of machine code. One compile run has one obverse input and one reverse output.


The core idea, in one sentence

The obverse is never semantically interpreted. Robby may now perform an explicit, user-triggered measurement pass over the source bytes—palette, luminance, texture, spatial colour, hashes, and embedded-evidence states—but it does not attempt to identify or understand what the photograph depicts. The reverse remains generated from explicit, inspectable inputs.

This is a deliberate, load-bearing rule, not an implementation detail. See TECH-SPEC.md for exactly what that does and does not permit.


How it works, conceptually

  1. You write a short script describing an image-object:

    base("night-street.jpg")
    palette(k: 8)
    reverse(mode: "negative")
    output(obverse: "night-street.jpg", reverse: "transient", manifest: "transient")
    
  2. troid, the Rust compiler engine inside robby, parses this into an intermediate representation (IR), validates it, and holds it ready.

  3. When you view the obverse and choose to flip it, the compiler runs, live, in that moment:

    • Computes the SHA-256 of the obverse's raw bytes.
    • Extracts a small number of dominant colour swatches — pure numerical clustering on pixel colour values, nothing more.
    • Derives a seed from the obverse's hash and your script's settings.
    • Feeds that seed into a deterministic art-rendering module, which produces the reverse at exactly the obverse's dimensions. The rule is enforced by the renderer: an optional base(width:, height:) declaration that disagrees with the measured source is a compile error, never a silent resize.
  4. The same obverse, with the same settings, always produces the exact same reverse — byte for byte. Change so much as one pixel of the obverse (a resize, a crop, a recompression) and the reverse changes completely. This is intentional: the reverse is a fingerprint of the obverse's exact, unaltered state.

The canonical reverse is generated by Rust as a transient PNG and manifest. The gallery may present that same compiler result through a p5.js canvas: p5 receives only the manifest's palette and derived seed, then animates a square k × k swatch matrix anchored to the left of the reverse face — bursts of individual cell-pair swaps (k/2 pairs across k/4 seconds, then a half-second rest), never whole rows or columns. When the obverse carries GPS evidence, a generalized terrain surface occupies the panel to the matrix's right. It is a presentation layer, not a second compiler or an alternative source of truth. Users who prefer a still image, including those who request reduced motion, see the canonical Rust PNG.


Why the reverse is not just "the negative of the image"

Early prototypes derived spatial structure from depicted content. That direction was removed because it required the compiler to understand the photograph rather than compile from explicit, inspectable inputs.

The current design insists that generating the reverse must never require the compiler to know what is in the picture. It only needs to know what colours are in it, and what its exact bytes are.


What you can inspect

The live site doesn't just show you the obverse and reverse — it shows the compiler's work in four modes:

Mode What it shows
Evidence The literal step-by-step compilation trace.
Pedagogic The same trace, rewritten in plain language for people with no compiler background.
Diff What changed between two compiled versions of the same script.
Failure What a compile error looks like, and why it happened.

There's also a live, in-browser source editor: you can edit the script for any specimen and recompile it against the real Rust compiler core — not a simulated preview. Around the deck sit the supporting pages: /compiler walks the eight-station pipeline end to end, /manual is the .robby language reference, /concept tells the two-sided-object story, /faqs answers the obvious questions, and /about carries the download links and author loop.

The Teppanyaki Counter has three views: Baking shows the live compilation stations (00–07, Intake through Marry) as one continuous processing rail with per-stage tick labels; Ingredients runs a bounded Rust/WASM analysis only when requested and exposes the palette, hashes, luminance, spatial, edge, texture, and perceptual fields; Stuffing reports EXIF, IPTC, XMP, GPS, and C2PA extraction states without exposing raw metadata or GPS coordinates. These views explain the one reverse artwork — they do not generate alternate reverse images.

GPS-bearing obverses also unlock a generalized terrain view (and the reverse-face terrain panel): a deterministic 16×16 heightfield. The terrain is gated by GPS presence but seeded by the source bytes, so the published field can never be inverted to the photo's coarse location band — raw coordinates never leave the intake module.

The proposed evidence-aware object language is documented as a versioned v2 direction in docs/ingredients-and-evidence.md. The current v1 parser does not silently accept that future syntax.

Running the engine and observation deck

From a clean checkout, the Rust engine can be used independently:

cargo build --release --locked
./target/release/troid check   examples/example-v1.robby
./target/release/troid compile examples/example-v1.robby --out /tmp/example-v1.ir.json

The recipe language is .robby; troid is the only binary the project ships. To run the full observation deck locally, install the web dependencies and start the development server:

pnpm install --frozen-lockfile
pnpm dev

The server watches gallery/ for JPEG inputs and matching .robby recipes. Gallery originals are local runtime inputs and are ignored by Git; no transformed or WebP source is accepted. Turning an image requests a fresh transient reverse from the Rust renderer. No reverse PNG is written to the repository, gallery, storage, or database.

The observation deck deliberately pauses briefly between each station's started, artifact, and completed events. This is a UI-only explainability aid: it gives the viewer time to watch the chain move from Intake through Marry. The pause is cancellable when changing specimens, and it does not affect troid inputs, output bytes, hashes, cache keys, or manifests.


Install prebuilt binaries

Pre-built troid binaries for macOS (Apple Silicon), Windows, and Linux (x86_64) are published on every release tag at https://github.com/thecont1/robby/releases/latest. The Linux x86_64 and macOS Apple Silicon downloads are stable URLs and always point at the most recent release:

Verify the archive you downloaded against SHA256SUMS.txt — from the same release — before unpacking it. Extract only your platform's line from the manifest and abort on any mismatch; the archive is unpacked only after verification succeeds:

# Download the checksum manifest next to the archive
curl -LO https://github.com/thecont1/robby/releases/latest/download/SHA256SUMS.txt

# macOS Apple Silicon
grep ' troid-macos-aarch64.tar.gz$' SHA256SUMS.txt | shasum -a 256 -c - \
  && tar -xzf troid-macos-aarch64.tar.gz

# Linux x86_64
grep ' troid-linux-x86_64.tar.gz$' SHA256SUMS.txt | sha256sum -c - \
  && tar -xzf troid-linux-x86_64.tar.gz
# Windows x86_64 (PowerShell) — throws before Expand-Archive on mismatch
Invoke-WebRequest https://github.com/thecont1/robby/releases/latest/download/SHA256SUMS.txt -OutFile SHA256SUMS.txt
$expected = (Get-Content .\SHA256SUMS.txt | Where-Object { $_ -match ' troid-windows-x86_64\.zip$' }).Split(' ')[0]
if ((Get-FileHash .\troid-windows-x86_64.zip -Algorithm SHA256).Hash.ToLower() -ne $expected) {
  throw "SHA-256 mismatch — refusing to unpack a corrupt or tampered archive"
}
Expand-Archive .\troid-windows-x86_64.zip -DestinationPath .

A typical v1 author loop from a clean checkout:

# 1. Download, verify, and unpack
curl -LO https://github.com/thecont1/robby/releases/latest/download/troid-macos-aarch64.tar.gz
curl -LO https://github.com/thecont1/robby/releases/latest/download/SHA256SUMS.txt
grep ' troid-macos-aarch64.tar.gz$' SHA256SUMS.txt | shasum -a 256 -c - \
  && tar -xzf troid-macos-aarch64.tar.gz

# 2. Write a recipe that points at your JPEG
cat > example.robby <<'RECIPE'
base("night-street.jpg")
palette(k: 8)
reverse(mode: "negative")
output(obverse: "night-street.jpg", reverse: "transient", manifest: "transient")
RECIPE

# 3. Validate, then compile to IR JSON
./troid check   example.robby
./troid compile example.robby --out example.ir.json

The recipe-check and recipe-compile commands cover the v2 object-recipe form documented in docs/ingredients-and-evidence.md.


Project status

This is an active hackathon build, developed in public. The constitutional purge is complete: the compiler core, native renderer, WASM bridge, server, gallery catalogue, and UI now use the same opaque-input, deterministic model. See TECH-SPEC.md for the exact contract and verification surface.


Where this comes from

The reverse/obverse concept originates from an unfinished 2023 MFA photography project exploring digital images as two-sided objects — a visible face for presentation, and a distinct reverse carrying its own meaning, in the spirit of a photograph's physical backing: a stamp, a caption, a provenance mark. robby is the technical completion of an idea that, at the time, outran the tools available to build it.


License / attribution

See repository metadata. Built by Mahesh Shantaram for SegFault 2026.

About

robby is a tiny explainable compiler for text-based visual compositions, producing obverse and reverse images plus a process manifest.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages