Doop
Doop is an open-source multiplayer design canvas and design MCP for Claude Code, Codex, and any AI agent. Design side by side with agents — free in beta.
A black-and-white print that carries its own color.
DesignPython · ShellGitHub @TonyD365OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
A black-and-white print that carries its own color. Print photos and documents on any B/W printer; scan them, and the color comes back.
Taken from the repository README
Loading the full README.
Read the full README ↗Print a color photo or document on any black-and-white printer. It looks like an ordinary grayscale page. Scan it, and Inkhue brings the color back.
Download · How to use · How it works · Benchmark · Wiki · 中文说明
Original · what gets printed (inset: the actual printer dots) · color recovered from the scan, at 2× resolution.
Original · printed · recovered. Bottom row: someone wrote on the printout with a pen — the color still comes back, and the handwriting stays, just as it is on the paper.
Documents too: notes, worksheets and slides keep their highlights, colored text and charts. Photos: public domain / CC0 (NASA, scikit-image).
Upside down, a little crooked, written on? All fine.
| Platform | Installer | Installs to |
|---|---|---|
| macOS (Apple silicon) | Inkhue-vX.Y.Z-macos-arm64.pkg | Applications |
| macOS (Intel) | Inkhue-vX.Y.Z-macos-x64.pkg | Applications |
| Windows 10/11 (64-bit) | Inkhue-vX.Y.Z-windows-x64-setup.exe | your programs folder (or Program Files for all users) |
| Ubuntu / Debian (x86-64) | Inkhue-vX.Y.Z-linux-x64.deb | /opt/Inkhue, app menu, inkhue command |
From the latest release. Every file is listed in
SHA256SUMS.txt and carries a build provenance attestation
(gh attestation verify <file> -R TonyD365/Inkhue).
macOS — open the .pkg. The first time, macOS refuses it: System Settings → Privacy & Security → Open
Anyway, then follow the installer. Once only; updates install themselves without a password.
Windows — run the setup.exe. If SmartScreen appears: More info → Run anyway.
Ubuntu / Debian — sudo apt install ./Inkhue-*.deb. Scanning uses SANE: sudo apt install sane-utils.
More help: installation · printing and scanning · troubleshooting · FAQ
flowchart LR
A[Color image] --> Y[Brightness]
A --> C[Color, 1/8 resolution]
C --> RS[Reed-Solomon + repetition<br/>+ public scatter]
Y --> H{{4×4-dot cells}}
RS --> H
H -->|dot count = brightness<br/>ink lean = 2 bits| P[Black-and-white page]
P --> S[Scan] --> D[Find frame, read cells,<br/>correct errors] --> N[Neural refiner] --> O[Color image, 2×]
A 600 dpi printer can only put a dot or leave paper white. Inkhue groups the dots into cells of 4×4 dots (1/150 inch). Each image pixel becomes one cell, and its brightness is the number of inked dots, 0 to 16 — the same idea as an ordinary halftone. Error diffusion spreads the rounding error to neighbouring cells so smooth gradients stay smooth.
Which of the 16 positions get ink is chosen from the image at full printer resolution: dots go where the picture is darkest inside the cell, so edges and fine detail survive inside a cell instead of being averaged away.
The arrangement also carries data. The centre of mass of the ink in a cell leans towards one of its four corners: left/right is one bit, top/bottom is another. The placement in step 1 is only allowed to choose arrangements whose centre of mass leans clearly enough (a safety margin) towards the corner the data asks for; the eye, seeing a 1/150-inch cell from reading distance, can't tell the difference.
That is 2 bits per cell, about 45,000 bits (5.6 KB) per square inch at 600 dpi — and the bits sit inside the picture, region by region, not in a separate code.
A cell with 0 dots (white paper) or 16 dots (solid black) has no corner to lean towards, so it can't carry bits. Photos have few such cells; a page of notes is mostly white. When more than 20 % of a page would be empty or full, Inkhue keeps 1 to 15 dots in every cell: blank paper gets one faint dot per cell and carries data like everything else. A flag in the header tells the decoder to rescale brightness, so white comes back white.
The page is surrounded by a white margin, a 2-cell black frame, a timing track of alternating black and white cells along the top and left, and a white gap.
For every cell Inkhue measures the ink (→ brightness) and the ink's centre of mass (→ two soft bits: not just 0 or 1, but how confidently). Slow drifts from imperfect alignment are removed. All copies of each bit are added up, weighted by confidence, then Reed-Solomon corrects what's left. A block that is still too damaged leaves its part of the picture gray instead of filling it with random color.
Decoded straight, the photo would be grainy and the color soft (1/8 resolution). A small U-Net (≈1 million parameters) reads the scan dot by dot together with the decoded color and outputs the photo at 2× the cell resolution (300 ppi for a 600 dpi print). It learns to remove the halftone grain, undo toner spread, read the detail that step 1 put inside each cell, and sharpen the color along brightness edges. It was trained on the 800 DIV2K photos and synthetic documents (colored text, highlights, charts), printed and scanned through a simulator with random printer and scanner settings. It ships as a 4 MB ONNX model and runs on the CPU in about a second.
The simulator reproduces toner spread, uneven coverage, paper noise, rotation, keystone, optical blur, tone curves, sensor noise and JPEG compression; the benchmark runs every change through it at several strengths.
How it differs from QR codes and watermarks: those store a few bytes to a few kilobytes, either next to the image or as a faint signature inside it. Inkhue stores information for every region of the picture while the page itself stays a clear photograph.
Every pull request is benchmarked against main: 4 public-domain photos and a text document × 3 seeds, through a
simulated printer and scanner (scripts/benchmark.py). Current main:
| Condition | Decoded | Raw bit errors | ΔE neural | Detail (PSNR @2×) |
|---|---|---|---|---|
| ideal | 100 % | 8.4 % | 2.66 | 28.8 dB |
| typical | 100 % | 8.1 % | 2.52 | 29.2 dB |
| 600 dpi scan | 100 % | 7.8 % | 2.91 | 28.1 dB |
| 400 dpi scan | 100 % | 12.7 % | 3.25 | 27.1 dB |
| handwriting on the page | 100 % | 9.4 % | 3.31 | 22.9 dB |
| harsh (poor print + scan) | 80 % | 19.9 % | 6.65 | 21.9 dB |
ΔE is CIEDE2000 color error (≈2 is barely visible). These are simulations; real-world reports are the most valuable contribution — see below.
git clone https://github.com/TonyD365/Inkhue && cd Inkhue
python3 -m venv .venv && .venv/bin/python -m pip install -r requirements-app.txt
.venv/bin/python -m inkhue # the app
.venv/bin/python -m inkhue.cli encode photo.jpg page.png # command line: encode / simulate / decode
Tests, benchmark, training the refiner and releasing: Development.
Pages printed today must still decode in ten years. The format constants — above all the scatter SEED — are frozen
and pinned by tests/test_format.py; every change since 1.0 has been backwards compatible.
The most useful thing you can do: print a page and scan it, then share the result with the 🖨️ print / scan report template. Code contributions: see CONTRIBUTING.md. Questions and ideas: Discussions. Security: SECURITY.md.
Apache-2.0 — free to use, modify and ship, including commercially. Published openly so the method stays free for everyone. Inkhue was called ColorHide until version 1.1.