Compares characterisation data from two ICC-profiling CSV files, showing 3D gamut shells, 2D gamut slices, and per-patch ΔE statistics.
Live at chardata.colourbill.com
npm install
node server.js
# → http://localhost:3001No build step for JS — server.js is a static file server only.
Everything of substance is in public/index.html. The Express server
only serves static files and sets the correct MIME type for .wasm.
Gamut computation (model fitting, 3D mesh, 2D slice) runs in C++ compiled to WebAssembly:
| File | Purpose |
|---|---|
gamut-wasm/gamut-wrapper.cpp |
C++ source — three embind-exported functions |
gamut-wasm/CMakeLists.txt |
CMake build config for Emscripten |
public/wasm/chardata-gamut.mjs |
Emscripten JS glue (committed build artifact) |
public/wasm/chardata-gamut.wasm |
WASM binary (committed build artifact) |
public/gamut.js |
JS wrapper — loads WASM, exposes window.Gamut |
A second, optional WASM module wraps IccProfLib (from iccDEV) for ICC profile header + tag display. It is lazy-loaded only when the user clicks Display File on an ICC slot, so the cold load stays at the gamut module's ~500 KB:
| File | Purpose |
|---|---|
icc-viewer-wasm/wrapper.cpp |
C++ source — validateProfile + describeTag embind exports |
icc-viewer-wasm/CMakeLists.txt |
CMake build, compiles IccProfLib sources directly |
public/wasm/icc-viewer.mjs |
Emscripten JS glue (committed) |
public/wasm/icc-viewer.wasm |
WASM binary (committed) — ~730 KB, lazy |
public/icc-viewer.js |
JS wrapper — window.IccViewer.{validateProfile, describeTag} |
Requires WSL (Ubuntu) with Emscripten and nlohmann-json installed.
One-time WSL setup:
# Install Emscripten
git clone https://github.com/emscripten-core/emsdk.git ~/emsdk-install/emsdk
cd ~/emsdk-install/emsdk && ./emsdk install latest && ./emsdk activate latest
source ~/emsdk-install/emsdk/emsdk_env.sh
# Install nlohmann/json
sudo apt install nlohmann-json3-devBuild:
# From WSL, at the repo root:
scripts/build-wasm.sh # gamut module (lcms2)
scripts/build-icc-viewer-wasm.sh # ICC viewer module (IccProfLib from iccDEV)The ICC viewer build expects iccDEV at /home/colour/code/iccdev (override with ICCDEV_ROOT=...). Artifacts are written to public/wasm/. Commit them alongside any C++ source changes — the Lightsail server has no build toolchain.
The pre-commit hook handles this automatically: if any file under gamut-wasm/ or icc-viewer-wasm/ is staged, the hook runs the matching build script via WSL and stages the rebuilt artifacts before the commit completes. A build failure aborts the commit.
After cloning, run once to activate the pre-commit hook:
git config core.hooksPath hooksThe hook script is tracked in hooks/pre-commit; only the git config
pointer needs to be set manually.
Pushes to main auto-deploy to Lightsail via GitHub Actions
(.github/workflows/deploy.yml). The workflow SSHs into the server,
does git pull, runs npm install --omit=dev, and restarts the pm2
process. No build step on the server.
Required GitHub Actions secrets: SSH_HOST, SSH_USER, SSH_PRIVATE_KEY.
To roll back to the pure-JS version, check out the legacy/compare
branch — that preserves the app as it was before the WASM migration.
Required columns: CYAN, MAGENTA, YELLOW, BLACK, LAB_L, LAB_A, LAB_B