ifslib
ifslib is a standalone WebAssembly library for parsing and rendering fractals described in the AIFS language. It powers every fractal canvas on this site. Embed it in any web page, Node.js script, or WASM-capable environment — no dependencies, no bundler required. For analytics (Hausdorff dimension, neighbor graph, boundary IFS generation), see the Advanced API →
Download
The WASM binary is served alongside this site and is part of the open-source repository:
- ifslib.wasm — download raw from GitHub
- ifslib-worker.js — reference Web Worker implementation (progressive + multi-canvas)
Instantiation
ifslib is a WASI reactor with zero imports.
Pass an empty imports object and call _initialize() once:
const resp = await fetch('ifslib.wasm');
const buf = await resp.arrayBuffer();
const { instance } = await WebAssembly.instantiate(buf, {}); // no imports needed
const wasm = instance.exports;
wasm._initialize(); // must call once after instantiation For repeated renders, compile the module once and instantiate a fresh copy per render (ifslib has a single global renderer per instance):
const mod = await WebAssembly.compile(buf); // compile once, cache
// later:
const { exports: wasm } = await WebAssembly.instantiate(mod, {});
wasm._initialize(); API Reference
Memory helpers
ifslib uses a C string ABI. Use malloc / free from the
exports, and the memory export to read/write bytes:
function writeCString(wasm, str) {
const bytes = new TextEncoder().encode(str + '\0');
const ptr = wasm.malloc(bytes.length);
new Uint8Array(wasm.memory.buffer).set(bytes, ptr);
return ptr; // caller must free()
}
function readCString(wasm, ptr) {
if (!ptr) return '';
const mem = new Uint8Array(wasm.memory.buffer);
let end = ptr;
while (mem[end] !== 0) end++;
return new TextDecoder().decode(mem.subarray(ptr, end));
} Core rendering pipeline
| Export | Signature | Description |
|---|---|---|
_initialize() | () → void | Must be called once after instantiation. Initialises the global renderer. |
init(ptr) | (i32) → i32 | Parse an AIFS source string (C string pointer). Returns the number of blocks found (≥1) on success, 0 on parse error. if (!wasm.init(ptr)) still works as a failure check. Error text via get_last_output(). |
get_block_idx(block) | (i32) → i32 |
Resolve a block string ID to its 0-based internal index without selecting it.
Accepts the block's string ID (the @id part of @id:parentId).
Pass nullptr (pointer 0) or empty string to get the index of the first non-hidden block.
Returns the index on success, or −1 if not found. Must be called after init().
|
set_block(block_idx) | (i32) → i32 |
Select a block by its 0-based numeric index (as returned by get_block_idx).
Pass −1 to select the first non-hidden block.
Resets the root set to the block default ($root if defined, otherwise first visible variable).
Can be called multiple times to switch blocks without calling init() again.
Returns the number of variables in the block (≥1) on success, or 0 on error.
|
set_root(root) | (i32) → i32 |
Override the root attractor set within the already-selected block (C string).
Must be called after set_block.
Can be called multiple times to switch root variables without calling set_block() again.
Returns the Euclidean dimension of the projected attractor (DIM ≥ 0) on success, or −1 on failure.
|
get_var_name(var_idx) | (i32) → i32 |
Return the name of the variable at 0-based var_idx within the currently selected block.
Returns ALL variables (map definitions and attractor sets alike) — not just attractors.
Returns a pointer to a null-terminated string valid until the next ifslib call, or 0 (null) if out of range or no block selected.
Must be called after set_block.
|
set_camera(params, n) | (i32, i32) → i32 |
Override the viewport for subsequent render() calls.
n=4: 2D — [cx, cy, r, angle_deg];
n=10: 3D — [loc.x,y,z, ref.x,y,z, up.x,y,z, fov_deg];
n=0: reset to automatic camera.
Must be called after set_block. Returns 1 on success.
|
render(w, h, quality, thickness) | (i32, i32, i32, i32) → i32 |
Render to an RGBA pixel buffer of w×h×4 bytes.
Returns a pointer to the buffer (valid until the next render call), or 0 on failure.
quality ≥ 1 (2 is usually sufficient); thickness ≥ 1 pixels.
|
get_last_output() | () → i32 | Returns a pointer to accumulated console output since the last call. Valid until the next ifslib call. |
For analytics functions — Hausdorff dimension, attractor metrics, neighbor graph, and boundary IFS — see the Advanced API.
Minimal Example
Render a 256×256 Sierpiński triangle and draw it onto a <canvas>:
const aifs = `@
$dim=2
f1=[0.5,0,0,0.5]
f2=[0.5,0]*[0.5,0,0,0.5]
f3=[0.25,0.5]*[0.5,0,0,0.5]
S=(f1|f2|f3)*S`;
const resp = await fetch('ifslib.wasm');
const { instance } = await WebAssembly.instantiate(await resp.arrayBuffer(), {});
const wasm = instance.exports;
wasm._initialize();
// write AIFS source into WASM memory
const bytes = new TextEncoder().encode(aifs + '\0');
const ptr = wasm.malloc(bytes.length);
new Uint8Array(wasm.memory.buffer).set(bytes, ptr);
if (!wasm.init(ptr)) throw new Error(readCString(wasm, wasm.get_last_output()));
wasm.free(ptr);
// pass -1 to select the first non-hidden block
if (!wasm.set_block(-1)) throw new Error('set_block failed');
const W = 256, H = 256;
const pixPtr = wasm.render(W, H, 2, 1); // quality=2, thickness=1
if (!pixPtr) throw new Error('render failed');
const rgba = new Uint8ClampedArray(wasm.memory.buffer, pixPtr, W * H * 4);
const canvas = document.querySelector('canvas');
canvas.width = W; canvas.height = H;
canvas.getContext('2d').putImageData(new ImageData(rgba, W, H), 0, 0); Note: to select the default block, pass −1 to wasm.set_block(-1).
To select by block ID, call get_block_idx(ptr) first to get the index, then pass it to set_block(idx).
set_root is only needed when you want a specific root set other than the block default.
Both functions can be called repeatedly to switch blocks/roots without reinitialising.
Using in a Web Worker
For non-blocking rendering (required for large canvases), run ifslib inside a Web Worker. The reference implementation used by this site supports:
- Progressive rendering (32 → 64 → … → final size) with early preview
- Multiple canvases rendered concurrently in round-robin
- Cancel support (leaves viewport → stops rendering)
- Module caching (compiled once, re-instantiated cheap per render)
See ifslib-worker.js for the full implementation.
Using in Node.js
import { readFile } from 'node:fs/promises';
const buf = await readFile('ifslib.wasm');
const { instance } = await WebAssembly.instantiate(buf, {});
const wasm = instance.exports;
wasm._initialize();
// ... same API as browser Tested on Node.js 18+. No WASI polyfills needed — the binary has no external imports.
Embedding on Your Own Site
-
Copy
ifslib.wasm(and optionallyifslib-worker.js) into yourpublic/(or static assets) directory. -
Serve the WASM file with the correct MIME type:
application/wasm. Most static hosts (GitHub Pages, Netlify, Vercel, Cloudflare Pages) do this automatically. -
ifslib does not use
SharedArrayBufferand has no cross-origin isolation requirements. No special response headers are needed.
License
ifslib.wasm is the computational core of the
IFStile
project, created by
Dmitry Mekhontsev
and distributed under the
GNU Lesser General Public License v3 (LGPL-3.0).
This means you can embed it in projects under any license (open-source or proprietary)
without affecting your code, as long as modifications to ifslib itself
remain open-source.