- TypeScript 65%
- Rust 17.3%
- CSS 11.1%
- JavaScript 2.7%
- Python 1.7%
- Other 2.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
Some checks failed
CI / frontend (push) Has been cancelled
CI / rust (push) Has been cancelled
Release / build-linux (push) Has been cancelled
Release / build-windows (push) Has been cancelled
Release / build-macos (push) Has been cancelled
CI / desktop-linux (push) Has been cancelled
CI / desktop-windows (push) Has been cancelled
CI / desktop-macos (push) Has been cancelled
Release / publish (push) Has been cancelled
A new algorithm that renders the image as characters — any script, any font. The engine is the single visual truth, but it has no fonts, so the split runs where it should: JS rasterises each character into a coverage bitmap, and the resulting atlas is shipped to the engine resident, like the pixel planes. It re-ships only when the characters, font or cell size change, never per slider tick, and preview, PNG export and video export all render through the same path — so what the stage shows is what the file contains. Fifty scripts: Latin, Greek, Cyrillic, Hebrew, Arabic, Syriac, Devanagari, Bengali, Tamil, Telugu, Thai, Tibetan, Khmer, Hiragana, Katakana, Hangul, Han, Ethiopic, Tifinagh, Vai, Cherokee, Runic, Braille, box drawing, block elements, maths, arrows and the rest. Or everything the font can draw at once. Or only the characters typed into the box. Two filters are what make "everything" mean anything. A font asked for a character it lacks draws the missing-glyph box, and every such character draws the *same* box, so without filtering "all scripts" under a Latin font yields a thousand identical rectangles; each candidate is compared against a known- absent codepoint and dropped if it matches. And many characters are simply indistinguishable at a 7x12 cell, so glyphs are merged on their quantised coverage. What survives is spread evenly from lightest to darkest, because an atlas that cannot reach paper-white loses the image's tonal range. The default font chains the Noto families rather than naming one. CSS falls back per character, so each glyph is drawn by whichever font actually has it — without that, picking "everything" under Roboto Mono silently reduces the repertoire to Latin, which is the trap this feature would otherwise walk into on first use. Matching is either shape, which picks the character whose strokes sit where the cell's detail sits and keeps edges as edges, or brightness, the classic ramp. Cell width and height are independent, and type size, weight, glyph count, ink curve, inversion and ink colour are all exposed. The cheap knobs are per-pass; anything that rebuilds the atlas is global, because one atlas is resident and two passes cannot each have their own. Strings translated into all seventeen other locales. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
| .cargo | ||
| .github/workflows | ||
| assets | ||
| dither-wasm | ||
| packaging | ||
| public | ||
| screenshots | ||
| src | ||
| src-tauri | ||
| templates | ||
| test | ||
| testdata | ||
| tools | ||
| .gitignore | ||
| .npmrc | ||
| build-all.sh | ||
| build-win.ps1 | ||
| eslint.config.js | ||
| index.html | ||
| LICENSE | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| THIRD-PARTY.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
Dizako
A dithering studio. Load an image or a video, pick from 30 algorithms and 130 palettes, and decide which colours land in the shadows and which in the highlights.
Built with Tauri, React, and Material 3. Everything runs locally. No uploads, no tracking.
Algorithms |
Palettes |
Image |
What it does
Open an image or a video, choose an algorithm and a palette, tune until it looks right, then export. Sliders re-render live. A compare wipe shows original against dithered.
| Area | Includes |
|---|---|
| Error diffusion | Floyd-Steinberg, Jarvis, Stucki, Sierra, Atkinson, and friends |
| Ordered | Bayer, halftone screens, line screens, blue noise, checkerboard |
| Threshold / experimental | Hard threshold, random noise, Riemersma, dot diffusion, Omino-like |
| Palette layers | Colours stacked by tonal position, so you choose what goes where |
| Image controls | Exposure, contrast, gamma, saturation, hue, blur, sharpen (before dither) |
| Video | Scrub, play and dither footage frame by frame; export MP4, WebM or a PNG sequence |
Video
Drop in an MP4, MOV, WebM or MKV and the whole studio applies to it. The transport under the canvas scrubs and plays; playback dithers every displayed frame live at preview resolution and snaps back to full detail the moment you stop. The two handles on the scrub track set the export range.
Export renders in two phases. Every frame in range is dithered through a pool of workers - one per core, so a clip renders several times faster than a single pass could - and kept as a lossless PNG. The frames are then pushed into the system's own encoder on a fixed clock, so the result runs at exactly the source frame rate rather than at whatever speed the render managed.
| Format | Notes |
|---|---|
| MP4 | H.264 via the host encoder. The most portable result; offered when the system supports it |
| WebM | VP8/VP9. Open codecs, slightly larger files |
| PNG frames | One lossless PNG per frame in a folder. Always available, and the right input for ffmpeg or a compositor |
Which of the three you get is decided by what the machine can actually do, not assumed - the export dialog lists what is available, says why anything is not, and shows the frame count, working-set size and estimated file size before you commit to the job. Exported video is silent; audio is not carried through.
Limits: 20 minutes and 4 MP per frame. Longer or larger sources are refused with an explanation rather than attempted and abandoned.
Building
Each platform has its own build script. Both compile the WebAssembly engine, build the frontend, and then bundle the app; neither cross-compiles, so run the one that matches the machine you are on.
| Platform | Script | Produces |
|---|---|---|
| Linux | ./build-all.sh |
.deb, .rpm, .AppImage, Arch .pkg.tar.zst |
| Windows | .\build-win.ps1 |
portable dizako.exe, NSIS installer |
| macOS | ./build-all.sh macos |
prints the steps only, see below |
Shared dependencies
Needed on every platform:
- Core build tools: Node 22+,
pnpm, Rust (cargo), andwasm-pack. - Rust Wasm target:
rustup target add wasm32-unknown-unknown, or therust-wasmpackage viapacman.
Linux
./build-all.sh
The script checks for missing dependencies, builds every bundle, and finally installs the Arch package it just produced via pkexec pacman -U. Each bundle is best effort, so one failing packager does not kill the rest.
Additional Tauri dependencies: webkit2gtk-4.1, base-devel, curl, wget, openssl, appmenu-gtk-module, gtk3, libvips, libayatana-appindicator. (On Arch, ./build-all.sh offers to install these for you via pkexec pacman.)
Windows
Run from PowerShell on a Windows machine:
.\build-win.ps1
The script validates its tools, compiles the WASM engine, installs dependencies, runs the Tauri build, then collects both binaries into a fresh release-win/ directory and prints their sizes. You get two artifacts, and they are not interchangeable:
| Artifact | What it is |
|---|---|
dizako.exe |
Portable standalone build. Launches straight into the GUI with no console window, needs no installation. |
Dizako_<version>_x64-setup.exe |
NSIS installer. Setup wizard, Start menu entry, uninstaller. |
Additional Windows requirements:
- Rust MSVC toolchain plus the Visual Studio Build Tools (C++ workload). Windows builds use MSVC by default.
- WebView2, which is preinstalled on Windows 11 and on current Windows 10.
.cargo/config.toml pins a mingw linker for x86_64-pc-windows-gnu. That only applies when you explicitly target the GNU toolchain and is ignored by the default MSVC build; delete that section if you want to build the GNU target without mingw present.
Cross-building Windows binaries from Linux needs the NSIS and mingw toolchains (pacman -S mingw-w64-gcc nsis or equivalent) and pnpm tauri build --runner cargo --target x86_64-pc-windows-gnu --bundles nsis. This is untested, which is why running the script on Windows is the supported path.
macOS
Scaffolding only, never exercised. ./build-all.sh macos prints the steps: install the Xcode command line tools, then pnpm tauri build --bundles dmg,app.
Manual build
If you prefer not to use the scripts:
# Compile the WebAssembly engine first
wasm-pack build dither-wasm --release --target web --out-dir pkg
# Install frontend dependencies
pnpm install
# Run the app
pnpm tauri dev # development mode
pnpm tauri build # bundle into src-tauri/target/release/bundle
Frontend only: pnpm dev / pnpm build.
Tests
pnpm test # golden parity: the wasm engine against the committed manifest
pnpm lint
tools/smoke.py drives the running dev server through a real browser - image
import, video import, playback, canvas navigation, a full video export, and the
error dialog - and checks the canvas actually painted. It needs Playwright and a
Chromium binary and is not part of CI:
pnpm dev &
python tools/smoke.py http://localhost:1420/
Building the AppImage by hand? Set
NO_STRIP=1.bundle.targetsis"all", so a barepnpm tauri buildon Linux will try the AppImage, and linuxdeploy ships an oldstripthat chokes on the.relr.dynsections in current system libraries. Without it the bundle fails with an unhelpfulfailed to run linuxdeploy../build-all.shalready sets this for you.
Matugen theming
Dizako can take its accent colour from matugen:
- Copy
templates/matugen-template.jsoninto your matugen templates directory. - Point matugen at it:
[templates.dizako] input_path = "~/.config/matugen/templates/dizako-matugen.json" output_path = "~/.config/dizako/colors.json" - Run matugen, then pick Settings → Accent → Matugen in Dizako.
License
MIT — free to use, modify, and distribute.