Open the Filter Library to browse Built-in, My Filters and Online. Search names, descriptions, authors and tags, then preview candidates on the canvas. Apply Filter commits the selected candidate; Cancel or Escape restores the exact working filter. Selected tags match all. Favorites and built-in My tags are saved in this browser. Exporting a built-in includes its supplied tags and My tags in the portable document. Save updates a custom filter by ID; Save as new creates a separate copy. Rendering does not save changes.
Online cards show standardized Sample images. Selecting a card previews its filter on your current image. Apply Filter keeps it as an imported, unsaved filter; Save in Author creates an independent My Filter. Download PNG returns the portable package. Favorites do not install filters. Saved catalogue metadata is available while refreshing; Retry handles catalogue errors, and selecting a failed preview again retries its package. Packages are cached only for this page session.
Filter FabJS evaluates four formulas—red, green, blue, and alpha—for every pixel. Intermediate calculations use floating-point values; final channel output is clamped to 0–255.
Core variables
r g b a | Source channels, 0–255. |
c | Source channel matching the formula currently being evaluated. |
x y | Current pixel coordinates. |
nx ny | Normalized coordinates from 0–1; singleton dimensions use 0.5. |
cx cy | Centered coordinates from -1–1; singleton dimensions use 0. |
X Y | Image width and height. |
z Z | Current channel index and channel count. |
i | Perceptual RGB luminance. |
u v | Signed YUV chroma. Native float bounds are u: -55–55 and v: -78–78, with spans U=110 and V=156. Legacy imports retain Filter Factory's historic bound constants. |
d m M | Direction, centre distance, and half-diagonal distance. |
Controls and sampling
ctl(i) | Reads control 0–9 as a value from 0–255. |
val(i,a,b) | Maps a control into range a–b. |
map(i,v) | Maps through control pair 0–4. |
src(x,y,z) | Nearest-neighbour source sampling with clamped edges. |
srcWrap / srcMirror | Nearest sampling with repeating or mirrored edges. |
srcLinear(x,y,z) | Bilinear source sampling for smoother transforms and displacement. |
cnv(k1,…,k9,d) | Applies a 3×3 convolution and divides by d. |
Control widgets and display ranges
Edit controls configures each fixed control slot as a slider, numeric entry, toggle, or seed field. A display range, step, number format, and short unit make the runtime control easier to understand, while formulas still receive the same canonical 0–255 value. For example, a displayed value from 24–192 maps back into 0–255, and val(i,24,192) reconstructs that semantic value. The editor detects simple constant val() mappings and offers non-binding range guidance; conflicting or dynamic mappings are not inferred.
Numeric shaping
clamp, lerp, step, smoothstep, floor, ceil, round, fract, sign, bias, and gain. For lerp(a,b,t), t may be 0–1 or 0–255.
WebGPU numerical limitation
Mandelbrot rendering has a pre-existing CPU/WebGPU numerical difference: coordinate precision and rounding can produce sparse pixel differences, sometimes large near sensitive fractal boundaries, plus occasional one-byte shading differences. The reference hardware suite remains 84/85, with Mandelbrot field the sole failure. Use CPU rendering when matching CPU output exactly is required.
Procedural fields
hash2, valueNoise, perlin, worleyF1, worleyF2, fbm, turbulence, ridged, and periodicNoise. Noise functions return normalized values from approximately 0–1. mandelbrot and julia return bounded normalized escape-time fields.
Coordinates, gradients, and patterns
radius / angle, repeat / mirrorRepeat, gradient3 / gradient4, linearGrad, radialGrad, angularGrad, checker, and brick. Palette ramps interpolate explicit scalar stops; field gradients and patterns return 0–1 masks.
Analytic shape masks
line(x,y,ax,ay,bx,by,width,feather), circle(x,y,cx,cy,radius,feather), ring(x,y,cx,cy,radius,width,feather), box(x,y,cx,cy,width,height,rotation,feather), triangle(x,y,ax,ay,bx,by,cx,cy,feather), and grid(x,y,cellWidth,cellHeight,lineWidth,feather) return anti-aliased 0–1 masks. Coordinates and sizes use pixels; box rotation uses 0–1024 for one full turn.
Signed-distance composition
sdfCircle, sdfLine, and sdfBox return signed pixel distances: negative inside, zero at the boundary, and positive outside. Compose them with sdfUnion, sdfIntersect, sdfSubtract, or sdfSmoothUnion, then convert the field to a 0–1 mask with sdfFill or sdfOutline. Warped SDF Bloom demonstrates deterministic domain warping with noise.
Fractal masks
sierpinski(x,y,cx,cy,size,depth,feather) creates an equilateral Sierpiński gasket with depth clamped to 0–10. It folds barycentric coordinates into the selected child triangle at each level and feathers both the outer triangle and removed-hole edges, so Sierpiński Fractal remains deterministic and GPU-compatible.
Blend modes
multiply, screen, overlay, softLight, and difference. Each accepts either two arguments or an optional third opacity argument from 0–255.
Filter descriptions
The multiline Description beneath Controls is filter-level metadata, not a rendering input. It is preserved in Built-in filters, My Filters, and native JSON import/export; older filters without a description load with an empty field.
Operators and rendering
+ - * / %, comparisons, logical and bitwise operators, shifts, ternary condition ? a : b, and comma sequencing. Formula edits are validated after a short pause but do not interrupt typing or update the image automatically. Select Update Preview or press Ctrl/⌘ + Enter when the formula is ready. Controls not referenced by any formula are disabled automatically.
Image clipboard
Paste image loads the first image found on the clipboard. You can also press Ctrl/⌘ + V while focus is outside a text field. Copy image encodes the rendered output as an RGBA PNG, verifies that encoded transparency is present when the image contains alpha, and writes compatible PNG, HTML-image, and exact-PNG clipboard representations when the browser supports them. Its shortcut is Ctrl/⌘ + Shift + C. Some receiving applications ignore transparent clipboard formats and flatten the image; use Export PNG when the target application does this. Clipboard buttons may require browser permission and a trusted context such as HTTPS, localhost, or an approved local-file context.
Compiler and typed IR
Filter FabJS parses formulas into syntax trees and compiles them into a typed intermediate representation before rendering. Current channel formulas use scalar, integer, boolean, mask, and channel values; the IR schema also defines reserved composite value categories for engine extensibility. IR metadata remains renderer-neutral, and the WGSL analyzer makes the final WebGPU compatibility decision.
GPU rendering
The renderer selector offers Auto, GPU, and CPU. Auto compiles compatible typed IR into WGSL and runs it as one full-frame WebGPU compute submission. The diagnostic beside the selector distinguishes GPU eligibility, explicit CPU selection, and CPU fallback while showing the IR operation count, pass count, and exact fallback reason under Technical diagnostics. Phase 3.5 includes the Phase 3.5A ten-control, coordinate, repeat, and palette-ramp vocabulary, Phase 3.5B bounded Mandelbrot/Julia intrinsics, Phase 3.5C signed-distance composition, and Phase 3.5D diagnostics and performance workloads alongside procedural noise, patterns, polar sampling, and fixed 3×3 convolution, so every native built-in can use WebGPU. Device loss is recovered on the next GPU render; unavailable WebGPU, legacy integer filters, bitwise/shift/comma expressions, rnd(), rst(), get(), put(), or shader errors automatically fall back to the CPU Worker. WebGPU normally requires HTTPS or localhost; opening the file directly may select CPU fallback.
Preview and transparency
The image area uses a Photoshop-style light checkerboard so transparent pixels are clearly visible in Filtered, Original, and Split views. The surrounding stage keeps its darker checker pattern for contrast, making alpha previews easier to read before exporting, copying, or pasting the image.
Built-ins and files
The preset menu contains 52 WebGPU-compatible built-ins. Every built-in identifies Anthony Chimming as author. Layered Noise Benchmark and Warped SDF Bloom are grouped as deterministic performance benchmarks so the same image and controls can be timed with CPU and GPU selection. Sixteen formula-heavy built-ins may exceed the CPU work budget on sufficiently large images, but remain CPU-compatible on smaller images. Import automatically detects Filter FabJS native JSON projects and historic .afs files. Multiline AFS formulas and legacy line-continuation markers are reconstructed before parsing. AFS files use legacy integer-style math and therefore render on the CPU. Exported PNGs carry the current validated native-v2 filter. Open Image and drag-and-drop can import that filter while keeping the current source, open the PNG while ignoring it, or cancel. Third-party editors and optimizers may strip this portable provenance, which is not cryptographic proof of authenticity. Clipboard image operations do not preserve this metadata.
Development architecture
Since v2.1, Filter FabJS uses native ES modules during development. Parser, typed IR, GPU compiler, renderers, presets, file I/O, and UI controllers are separated to keep the codebase maintainable and renderer boundaries explicit. The build scripts can still produce a standalone HTML release.
Compatibility scope
This browser app targets RGBA images and common Filter Factory expressions. It is not a Photoshop plug-in and is not bit-exact for every historic edge case.
Support Filter FabJS
Filter FabJS is free to use and independently developed. If you find it useful, you can support continued development on Ko-fi. Contributions are entirely optional; the app remains free to use.
Distributed under GPL-2.0-or-later. Inspired by the open-source Filter Foundry project.