Reconstructs lost JPEG detail for smoother, more pleasing images — Rust rewrite of jpeg2png, ~3× faster, runs natively via CLI or directly in the browser via WASM.
JPEG compression discards data and regular decoders "fill in" the gaps with noisy guesses that create visible artifacts. Instead of patching holes, artefact re-optimizes the DCT coefficients with a regularized solver to produce smoother gradients with less staircasing.
Maintenance mode. Feature work is complete. The project receives automated dependency and security updates only — Dependabot runs daily with a 14-day cooldown, batched into weekly releases. Bug reports are welcome; new features and large changes are unlikely to be accepted.
- Rust core — port of
jpeg2pngfrom C++ to Rust (backend/artefact-core) - ~3× faster —
rayonparallelism + optional SIMD (std::simd, adaptive x8/x16/x32/x64 dispatch via thesimdfeature; wasm uses uniform x8) - GPU acceleration — optional
wgpubackend for native and browser solves, with tolerance-checked equivalence and explicit CPU fallback - WASM-ready —
backend/artefact-wasmviawasm-pack, runs 100% client-side at artefact.delnegend.com (no upload) - CLI + Web — same solver for native binary (
artefact-cli) and browser (frontendVue 3 + Vite) - Flexible I/O — input
.jpg/.jpeg, outputpng/webp/tiff/bmp(auto by extension) - Tunable solver — per-channel
weight/pweight/iterations,separate_componentsfor YCbCr
Pre-built CLI (recommended):
# download from Releases (output: artefact-cli, .exe on Windows)
# https://github.com/Delnegend/artefact/releases/latestBuild from source:
git clone https://github.com/Delnegend/artefact.git
cd artefact
# native CLI (release, uses mold + clang if in devcontainer)
cargo build --bin artefact-cli --release
# or: just build
# cross-compiled (linux x64, windows x64, macOS arm64)
# built on GitHub Actions via .github/workflows/release.yml — see DevelopmentWeb (no install):
Open artefact.delnegend.com — everything runs in your browser.
1. The performance way — CLI:
# basic: input.jpg -> input.png (same dir)
artefact-cli input.jpg
# choose output and format
artefact-cli input.jpg -o output.webp --format webp -y
# tune solver (single value for all channels, or Y,Cb,Cr)
artefact-cli input.jpg --weight 0.3 --pweight 0.001 --iterations 50
artefact-cli input.jpg --weight 0.3,0.2,0.3 --iterations 50,30,50
# benchmark without writing file
artefact-cli input.jpg --benchmark
# use the GPU when available, otherwise fall back to the CPU
artefact-cli input.jpg --gpu
# help
artefact-cli --helpSolver backends: process() always uses the CPU pipeline. The CLI’s --gpu
uses process_auto(): it logs the selected wgpu adapter and solving on GPU
when usable, otherwise warns and returns to solving on CPU pipeline. Set
RUST_LOG=debug for lower-level selection diagnostics.
To compare production solves without asserting benchmark timings:
RUSTFLAGS="-C target-cpu=native" RAYON_NUM_THREADS=8 \
cargo bench -p artefact-core --features bench,simd,gpu --bench gpu2. The convenience way — browser:
- Go to artefact.delnegend.com
- Drop a JPEG
- Compare input/output with the slider and download PNG
WASM is slower than native but stays fully client-side. The browser worker probes for a usable WebGPU adapter (including
GPUAdapter.info) and passes that decision to the asynccomputeAPI: GPU when available, CPU otherwise.
Recommended: devcontainer — no host toolchain needed:
# VS Code: Command Palette → Reopen in Container
# CLI:
devcontainer up --workspace-folder .
Toolchain is baked into the image (Rust nightly + rust-analyzer, mold 2.42.1, cargo-binstall/flamegraph/wasm-pack, just, fzf, bun, node) for cache and for editors that skip postCreateCommand (e.g. Zed). postinstall.sh only runs bun i in frontend.
Without devcontainer:
- Rust via
rustup(nightly,minimalprofile) just,bun,wasm-packzip/taronly if manually archiving — releases (linux x64, windows x64, macOS arm64) are built on GitHub Actions via.github/workflows/release.yml.ffmpegonly for sample image generation (not in devcontainer by default).
See docs/development.md for full prerequisites and sample-image helpers.
.
├── backend/
│ ├── artefact-core/ # core solver — pipeline/{scalar,simd,gpu} + shared utils
│ ├── artefact-cli/ # native binary (clap)
│ ├── artefact-wasm/ # wasm-pack cdylib for frontend
│ └── zune-jpeg/ # fork of zune-jpeg — exposes DCT coeffs + fixes
├── frontend/ # Vue 3 + Vite + Tailwind — src/utils/artefact-wasm is generated
├── assets/ # demo images (01.png-04.png)
└── docs/development.md # directory structure, SIMD flags, cross-compile, WASM/web builds
Workspace versions are centralized in [workspace.dependencies] at the root Cargo.toml:5 — bump once, inherited via workspace = true in each crate.
# frontend dev (hot reload)
just dev
# or: cd frontend && bun x vite
# WASM lib (generates frontend/src/utils/artefact-wasm)
just build wasm
# or: wasm-pack build backend/artefact-wasm --target web --out-dir frontend/src/utils/artefact-wasm
# web (static build for GitHub Pages -> frontend/dist)
just build web
# or: cd frontend && bun x vite build
# native CLI (release, LTO)
just build # -> target/release/artefact-cli
# or: cargo build --bin artefact-cli --release
# cross-compiled releases (linux x64, windows x64, macOS arm64)
# built on GitHub Actions via .github/workflows/release.yml
# trigger: weekly cron (Sunday 00:00 UTC) or workflow_dispatchSIMD / solver flags are toggled in backend/artefact-core/Cargo.toml features (simd) and enabled in dependent crates — see docs/development.md#solver-pipelines. Pipelines live in pipeline/{scalar,simd,gpu} with shared logic in utils/ (scalar is the frozen reference, simd is the default for the CLI and wasm).
just check # fmt + clippy + tests + oxlint + oxfmt (all)
just check rust # Rust only
just check js # frontend only (oxlint + oxfmt)just check rust sets ARTEFACT_REQUIRE_GPU=1, so GPU tests cannot pass by
skipping missing adapters or required fixtures. Plain cargo test keeps the
lenient skips for machines without a GPU.
Sample images with chroma subsampling:
just encode # assets/sample.png -> assets/sample.{j444,j422,j420,444,422,420}.input.jpg (needs ffmpeg)
just decode 420 # -> assets/sample.420.decoded.png via artefact-cli
just flame 420 # flamegraph for profilinggraph TD
Z[zune-jpeg<br/>fork - DCT coeffs] --> L[artefact-core<br/>solver<br/>pipeline/{scalar,simd,gpu}<br/>rayon]
L --> C[artefact-cli<br/>clap - png/webp/tiff/bmp<br/>--gpu optional]
L --> W[artefact-wasm<br/>wasm-bindgen<br/>cdylib<br/>process_auto/process]
W --> F[frontend<br/>Vue 3 / Vite<br/>Tailwind + PWA<br/>artefact.delnegend.com]
F -. upload .-> W
artefact-core is feature-gated: without simd, pipeline::scalar is used; simd selects the SIMD pipeline. Native uses adaptive x8/x16/x32/x64 dispatch over std::simd; wasm uses uniform x8. gpu adds the pipeline::gpu backend with process_gpu, process_gpu_with, and process_auto. Decoding always goes through the vendored zune-jpeg fork.
| Flag | Short | Default | Description |
|---|---|---|---|
<input> |
— | — | Input JPEG file |
--output <path> |
-o |
<input>.png |
Output file (extension infers format when --format auto) |
--format <fmt> |
-f |
auto |
auto or png/webp/tiff/bmp |
--weight <f32> |
-w |
0.3 |
2nd-order weight — higher = smoother, less staircasing. Single or Y,Cb,Cr |
--pweight <f32> |
-p |
0.001 |
Fidelity weight — higher = closer to source JPEG |
--iterations <n> |
-i |
50 |
Solver iterations — higher = better but slower. Single or Y,Cb,Cr |
--separate-components |
-s |
false |
Optimize Y/Cb/Cr separately instead of jointly |
--gpu |
-g |
false |
Use the GPU when available (process_auto), otherwise fall back to CPU |
--benchmark |
-b |
false |
Run solver but don't write output |
--overwrite |
-y |
false |
Overwrite existing output |
Defined in backend/artefact-cli/main.rs:18 and backend/artefact-core/lib.rs:63.
The project is in maintenance mode: bug fixes and dependency/security updates are welcome, but feature work is paused. Please open an issue before starting anything substantial.
git clone https://github.com/Delnegend/artefact.git
# devcontainer recommended, else install prerequisites above
just check # must pass before PRUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion shall be dual-licensed as below without additional terms (per Apache-2.0 §5).
Licensed under either of
- Apache License, Version 2.0 (LICENSE-Apache or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Based on jpeg2png by Victor van der Elst. Thanks to zune-jpeg / zune-image and the Rust / WASM / Vue / Vite communities.



