# numbl image filter A small web app that filters an image using a **[numbl](https://numbl.org)** script (MATLAB syntax), running entirely in your browser. Pick a built-in sample or upload your own image, write a filter function, and see the original and filtered images side by side. It uses [numbl](https://www.npmjs.com/package/numbl) as a library: the image is handed to the script as a tensor and the script's output tensor is drawn back to a canvas. ## Quick start ```bash npm install npm run dev ``` Then open the printed URL. ```bash npm run build # type-check + production build to dist/ npm run preview # serve the production build ``` ## How the filter works You write a function `filterImage` (MATLAB syntax). It receives the image and returns a new one: ```matlab function out = filterImage(img) % img : H x W x 3 array of doubles in [0, 255] (RGB) % out : H x W x 3 (color) or H x W (grayscale) in [0, 255] out = 255 - img; % invert end ``` - `img(:,:,1)`, `img(:,:,2)`, `img(:,:,3)` are the R, G, B planes. - Returning an `H x W` (2-D) array gives a grayscale result. - Output values are clamped to `[0, 255]` when displayed. - The function name doesn't have to be `filterImage` — the app calls whatever function the script defines. (A script with no `function` header is run as-is with `img` predefined and `out` read back.) Use the **Example** dropdown for ready-made filters: grayscale, sepia, posterize, contrast, channel swap, Sobel edges, box blur, and more. ## How it's wired ``` upload / sample image │ decode to RGBA (canvas) ▼ RGBA bytes ── rgbaToTensorData ──► [H,W,3] doubles (column-major, 0-255) ▼ Web Worker: executeCode("out = filterImage(img);", { initialVariableValues: { img } }) ▼ out tensor ── tensorToRaw ──► RGBA bytes ──► canvas ``` - Marshaling lives in [`src/imageConvert.ts`](src/imageConvert.ts). numbl tensors are **column-major**; browser `ImageData` is row-major RGBA — the conversion handles both. - The filter runs in a [Web Worker](src/filter.worker.ts) (`numbl`'s `executeCode` at optimization `"1"`, the JS-JIT, which is browser-safe) so full-resolution images with per-pixel scripts don't freeze the page. ## Images Everything runs locally — there are **no remote images**. Two sources: - **Generated** patterns (RGB gradient, shapes, checkerboard) created in the browser ([`src/synthetic.ts`](src/synthetic.ts) / [`src/samples.ts`](src/samples.ts)). - **Your uploads**, which are saved to **IndexedDB** and persist across sessions. They appear under "Your images" in the picker; each can be deleted with the × button. Storage lives in [`src/imageStore.ts`](src/imageStore.ts) (the original compressed file Blob + a small thumbnail are stored; the full image is decoded on demand). ## License Apache-2.0 (matching numbl). Sample photos are CC0 / public domain from Wikimedia Commons.