concept-collection / numbl-image-filter
numbl-image-filter
Go to fileHistoryFork
.githubInitial commit: numbl image filter web app
publicInitial commit: numbl image filter web app
srcInitial commit: numbl image filter web app
.gitignoreInitial commit: numbl image filter web app
index.htmlInitial commit: numbl image filter web app
package-lock.jsonInitial commit: numbl image filter web app
package.jsonInitial commit: numbl image filter web app
README.mdInitial commit: numbl image filter web app
tsconfig.app.jsonInitial commit: numbl image filter web app
tsconfig.jsonInitial commit: numbl image filter web app
tsconfig.node.jsonInitial commit: numbl image filter web app
vite.config.tsUse a relative base so the build works at any mount point

numbl image filter#

A small web app that filters an image using a numbl 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 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#

npm install
npm run dev

Then open the printed URL.

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:

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. numbl tensors are column-major; browser ImageData is row-major RGBA — the conversion handles both.
  • The filter runs in a Web Worker (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/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 (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.