npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ddot-wasm

v0.1.2

Published

WebAssembly bindings for ddot — high-performance image processing in the browser

Readme

⚡ ddot

A high-performance, modular image manipulation library written in Rust. Designed from the ground up for speed and flexibility, ddot provides a fast core engine, a dynamic CLI for automation, and high-quality WebAssembly (WASM) bindings for modern web applications.


📦 Architecture Overview

The repository is structured as a Cargo workspace containing the following crates:

  • core: The pure-Rust image processing engine. It defines standard image models, a dynamic filter architecture, palette generation algorithms, dithering, image resizing, and GPU compute dispatch.
  • cli: A robust command-line application that allows introspecting, generating schemas, and chaining multiple filters sequentially using JSON pipelines. Supports CPU and GPU (wgpu/WebGPU) backends.
  • wasm: High-performance WASM bindings that bridge the core Rust library directly to browser/Node.js environments via wasm-bindgen.
  • core-macros: Procedural macros (#[derive(Filter)] and #[derive(FilterParams)]) used to declare filters and auto-generate parameter schemas and validation logic.

🛠️ The Core Engine (ddot-core)

The core engine contains several dedicated modules:

1. Image Model

  • Image: A basic wrapper holding width, height, and a raw Vec<u8> pixel buffer ([r, g, b, a]).
  • Color: Represents individual RGB/RGBA pixels.

2. Filters

Built-in filters are declared via macros and registered in the FILTERS slice:

  • adjustment: Handles global levels adjustments. Supports CPU + GPU.
    • gamma (range: 0.3..3.0, default: 1.0)
    • blacks (range: -0.5..0.5, default: 0.0)
    • whites (range: -0.5..0.5, default: 0.0)
    • contrast (range: -100..500, default: 0)
    • saturation (range: 0.0..10.0, default: 1.0)
    • hue (range: -PI..PI, default: 0.0)
  • noise: Deterministic procedural noise filter. Supports CPU + GPU.
    • coverage (range: 0.0..1.0, default: 0.0)
    • intensity (range: 0.0..1.0, default: 0.0)
    • saturation (range: 0.0..1.0, default: 0.0)
  • gaussian_blur: Separable 1D Gaussian blur. Supports CPU + GPU.
    • radius (range: 0..50, default: 1)
    • sigma (range: 0.1..20.0, default: 1.0)
  • kawase_blur: Fast multi-pass Kawase blur. Supports CPU + GPU.
    • iterations (range: 1..10, default: 3)
    • offset (range: 0.5..5.0, default: 1.0)

3. Palette Generation

Exposes algorithms to analyze an image and generate optimized color palettes:

  • Median Cut: Standard box-splitting palette reducer.
  • Octree: Tree-based quantization.
  • K-Means: Iterative clustering for highly precise palette generation.

4. Dithering

Applies error-diffusion or ordered dithering based on a color palette. 11 algorithms available:

| Algorithm | GPU Support | Description | |---|---|---| | Floyd-Steinberg | ❌ CPU only | Classic error-diffusion | | Atkinson | ❌ CPU only | Soft error-diffusion (1/8 factor) | | Stucki | ❌ CPU only | High-quality error-diffusion | | Burkes | ❌ CPU only | Simplified Stucki variant | | Sierra | ❌ CPU only | 3-row error diffusion | | Sierra Two Row | ❌ CPU only | 2-row Sierra variant | | Sierra Lite | ❌ CPU only | Lightweight Sierra | | Jarvis-Judice-Ninke (JJN) | ❌ CPU only | Wide kernel diffusion | | Bayer | ✅ CPU + GPU | Ordered threshold matrix | | Random | ✅ CPU + GPU | Randomized ordered dithering | | OnlyPalette | ❌ CPU only | Nearest palette color, no diffusion |

5. Transform

  • Resize: Nearest Neighbor downscaler optimized for fast rendering pipelines.
  • Crop: Crop an image to a given bounding box (top, left, right, bottom).

6. GPU Backend

The core supports a wgpu/WebGPU compute backend with automatic CPU fallback:

  • Filters declare a WGSL compute shader via gpu_shader().
  • The CLI uses wgpu (native); the WASM target uses the browser's WebGPU API.
  • On auto mode: GPU is attempted, and falls back to CPU silently if unavailable.
  • On gpu mode: Falls back to CPU with a warning if WebGPU is unavailable.

💻 CLI Usage (ddot-cli)

The CLI offers a dynamic pipeline interface for automating image operations.

Installation

cargo build --release --bin ddot-cli

The binary will be at target/release/ddot-cli (or ddot-cli.exe on Windows).

1. List Available Filters

List all registered filters, their parameters, data types, default values, and valid ranges:

ddot-cli list

Example output:

Available filters:

  * Filter: adjustment
    Parameters:
      - gamma (float, default: 1, range: 0.3..3)
      - blacks (float, default: 0, range: -0.5..0.5)
      - whites (float, default: 0, range: -0.5..0.5)
      - contrast (integer, default: 0, range: -100..500)
      - saturation (float, default: 1, range: 0..10)
      - hue (float, default: 0, range: -3.1415927..3.1415927)

  * Filter: noise
    Parameters:
      - coverage (float, default: 0, range: 0..1)
      - intensity (float, default: 0, range: 0..1)
      - saturation (float, default: 0, range: 0..1)

2. Generate JSON Schema

Export a clean JSON template of a single pipeline step for a specific filter. Since it writes directly to stdout, you can redirect/pipe it to a configuration file:

# Output template to terminal
ddot-cli schema adjustment

# Direct redirection to a JSON file
ddot-cli schema adjustment > step.json

Output example (step.json):

{
  "name": "adjustment",
  "settings": {
    "blacks": 0.0,
    "contrast": 0,
    "gamma": 1.0,
    "hue": 0.0,
    "saturation": 1.0,
    "whites": 0.0
  }
}

3. Apply a Filter Pipeline

Apply one or more filters in sequence to an input image. The --pipeline flag accepts either an inline JSON array string or a path to a JSON pipeline file. Select the backend with --backend (or -b):

| Backend | Behavior | |---|---| | auto (default) | Tries GPU, falls back to CPU silently | | cpu | Always uses CPU | | gpu | Uses GPU; warns and falls back to CPU if unavailable |

Using inline JSON and CPU backend:

ddot-cli apply input.jpg -o output.png \
  --pipeline '[{"name": "adjustment", "settings": {"saturation": 0.0}}]' \
  --backend cpu

Using a pipeline file and GPU acceleration:

Create a pipeline configuration (pipeline.json):

[
  {
    "name": "adjustment",
    "settings": {
      "gamma": 1.2,
      "contrast": 15
    }
  },
  {
    "name": "gaussian_blur",
    "settings": {
      "radius": 3,
      "sigma": 1.5
    }
  },
  {
    "name": "noise",
    "settings": {
      "coverage": 0.3,
      "intensity": 0.05
    }
  }
]

Execute it on the GPU:

ddot-cli apply input.jpg -o output.png --pipeline pipeline.json --backend gpu

Note: If --output / -o is omitted, the output is saved next to the input as <stem>_ddot.png (e.g., photo.jpg → photo_ddot.png).


🌐 WebAssembly API (ddot-wasm)

Compile the WASM target using wasm-pack:

# Run the PowerShell build script
./build_wasm.ps1

Here is a guide on how to load and use the WASM library in JavaScript/TypeScript:

1. Loading & Manipulating Images

import { Image } from "ddot-wasm";

// Initialize from browser ImageData
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

const wasmImage = new Image(imageData);

// Accessors
console.log(wasmImage.width, wasmImage.height);
const pixels = wasmImage.pixels; // Uint8Array copy of the pixel buffer

// Deep copy
const copy = wasmImage.clone();

// Render back to canvas
ctx.putImageData(wasmImage.toImageData(), 0, 0);

2. Applying Filters Dynamically

import { Filters } from "ddot-wasm";

// Get all available filters as an Array with named properties
const filterHandles = Filters.getFilters();

// Access by name (named property on the array)
const adjustment = filterHandles.adjustment;
const gaussianBlur = filterHandles.gaussian_blur;

// Or get just filter names
const names = Filters.getFilterNames(); // ["adjustment", "noise", "gaussian_blur", ...]

// Inspect a filter handle
console.log(adjustment.name);           // "adjustment"
console.log(adjustment.supportsGpu);    // true
console.log(adjustment.backendSupport); // "cpuandgpu"
console.log(adjustment.getParams());    // Array of param descriptors

// Apply a filter asynchronously (await required)
await adjustment.apply(wasmImage, {
  gamma: 1.5,
  saturation: 0.8,
  contrast: 10
});

3. GPU Backend

import { Filters } from "ddot-wasm";

const noise = Filters.getFilters().noise;

// Check if a filter supports GPU
console.log(noise.supportsGpu); // true

// Access the WGSL shader source (if available)
console.log(noise.gpuShader);

// apply() dispatches to GPU via WebGPU when available.
// Falls back to CPU automatically.
await noise.apply(wasmImage, { coverage: 0.3, intensity: 0.05 });

4. Generating Color Palettes

import { Palettes } from "ddot-wasm";

const generators = Palettes.Generators; // { MedianCut, Octree, Kmeans }

// Median Cut — params: { n_of_colors: int (2..256, default 16) }
const palette = generators.MedianCut.calculate(wasmImage, { n_of_colors: 16 });

// Octree — params: { n_of_colors: int (2..256, default 16) }
const palette2 = generators.Octree.calculate(wasmImage, { n_of_colors: 8 });

// K-Means — params: { n_of_colors, max_iterations, tolerance }
const palette3 = generators.Kmeans.calculate(wasmImage, {
  n_of_colors: 16,
  max_iterations: 10,  // range: 1..100, default: 10
  tolerance: 0.1       // range: 0.00001..1.0, default: 0.1
});

// Read colors as array of { r, g, b, a } objects
console.log(palette.colors);

5. Dithering

import { Dithering } from "ddot-wasm";

// Access algorithms as a named object
const algs = Dithering.Algorithms;
// Keys: FloydSteinberg, Atkinson, Stucki, Burkes, Sierra,
//       SierraTwoRow, SierraLite, Jjn, Bayer, Random, OnlyPalette

// Or get as an Array
const algArray = Dithering.getAlgorithms();

// Error-diffusion algorithms — params: { amount: float (0.0..1.0, default 1.0) }
algs.FloydSteinberg.apply(wasmImage, palette, { amount: 1.0 });
algs.Atkinson.apply(wasmImage, palette, { amount: 0.8 });
algs.Stucki.apply(wasmImage, palette, { amount: 1.0 });
algs.Burkes.apply(wasmImage, palette, { amount: 1.0 });
algs.Sierra.apply(wasmImage, palette, { amount: 1.0 });
algs.SierraTwoRow.apply(wasmImage, palette, { amount: 1.0 });
algs.SierraLite.apply(wasmImage, palette, { amount: 1.0 });
algs.Jjn.apply(wasmImage, palette, { amount: 1.0 });

// Bayer — params: { amount: float (0..1), matrixScale: int (1..8, default 1) }
algs.Bayer.apply(wasmImage, palette, { amount: 1.0, matrixScale: 2 }); // GPU accelerated

// Random — params: { amount: float (0..1, default 0.65), seed: float (0..100, default 1.0) }
algs.Random.apply(wasmImage, palette, { amount: 0.65, seed: 42 }); // GPU accelerated

// OnlyPalette — no params (maps each pixel to the nearest palette color)
algs.OnlyPalette.apply(wasmImage, palette, {});

6. Transform

import { Transform } from "ddot-wasm";

// Resize (Nearest Neighbor)
const resized = Transform.Resize(wasmImage, { width: 300, height: 200 });

// Crop
const cropped = Transform.Crop(wasmImage, {
  top: 10,
  left: 10,
  right: 290,
  bottom: 190
});

🧪 Testing & Validation

Run unit tests for all crates:

cargo test

Run clippy checks:

cargo clippy --all-targets