Dithering effects for every layer of the web. A small toolkit of packages — pick the one that fits where you want the work to happen: browser, server, or algorithm layer.
ditherkit is a set of three npm packages for adding 1-bit and low-bit dithering effects to web projects. It supports Floyd-Steinberg, Atkinson, and threshold dithering with arbitrary color palettes, brightness / contrast pre-processing, and image resizing. Use it for retro visual styles, high-contrast photographic effects, accessibility-friendly image variants, or just because pixelated images look cool.
The packages are split so you only pay for what you use:
- Interactive in the browser? Use
@ditherkit/react. - Server-rendered and cached in a Next.js app? Use
@ditherkit/next. - Non-React, build script, edge runtime, or custom pipeline? Use
@ditherkit/coredirectly.
| Package | What it is | When to reach for it |
|---|---|---|
@ditherkit/core |
Pure algorithm functions. No DOM, no framework, zero runtime dependencies. | Build scripts, Svelte/Vue/Solid apps, Cloudflare Workers, custom pipelines. |
@ditherkit/react |
<DitheredImage /> React component backed by a shared Web Worker. |
Interactive use: live sliders, user uploads, real-time previews in any React app. |
@ditherkit/next |
<DitheredImageSSR /> server component, withDitherkit config helper, and pre-built route handler subpaths for App Router and Pages Router. Server-renders via Sharp and caches with ISR. |
Production Next.js apps where images should be cached, SEO-visible, and CDN-friendly. |
The most common path is a Next.js App Router app using @ditherkit/next:
pnpm add @ditherkit/next sharp// next.config.ts — the ONE place ditherkit is configured
import type { NextConfig } from 'next'
import { withDitherkit } from '@ditherkit/next/config'
const config: NextConfig = {
// your existing config
}
export default withDitherkit(config, {
routeBase: '/api/dithered',
})// app/page.tsx
import { DitheredImageSSR } from '@ditherkit/next'
import portrait from '@/assets/portrait.jpg'
export default function Page() {
return (
<DitheredImageSSR
src={portrait}
alt="A portrait, dithered on the server"
algorithm="Atkinson"
priority
/>
)
}// app/api/dithered/[cacheKey]/[...params]/route.ts
export { GET } from '@ditherkit/next/route/app'That's the whole setup. withDitherkit writes a build-time env
bridge that the component and the route handler both read at
runtime — no factory call, no shared lib module. The component
generates a deterministic URL, the route handler runs the Sharp +
dithering pipeline, and next/image handles the rest.
For Pages Router, only the route file changes:
// pages/api/dithered/[cacheKey]/[...params].ts
export { default } from '@ditherkit/next/route/pages'The withDitherkit call and <DitheredImageSSR /> import are
identical between routers. See
apps/example-next-pages for the mirror
example.
For interactive client-side use, or for non-Next.js stacks, see the
Getting started guide and
the per-stack pages under apps/docs/content/stacks/.
apps/example-next— Next.js App Router flagship. Demonstrates both kinds ofsrc(importedStaticImageDataand external URL) plus interactive client-side dithering with@ditherkit/react. Readable end-to-end in 60 seconds.apps/example-next-pages— Next.js Pages Router mirror, feature-for-feature identical to the App Router example.
Both example apps use the singleton pattern: zero lib/ files,
configuration declared once in next.config.ts via withDitherkit,
and the route handler is a single re-export.
More examples for Vite, Astro, Remix, and Node CLI are planned — see the backlog § D for the full list.
The documentation site lives at apps/docs and is built
with Fumadocs. Content is MDX under
apps/docs/content/:
/docs/getting-started— decision tree and first-working-example for each package/docs/core— algorithm reference, color utilities, image adjustments/docs/react—<DitheredImage />props, the shared-singleton Web Worker architecture, recipes/docs/next—<DitheredImageSSR />, route setup, ISR caching, StaticImageData cache keys, external URLs/architecture— why three packages, dependency graph, under-the-hood internals/stacks— per-stack status and integration guides (Next.js, React SPA, Remix, Astro, Svelte/Vue/Solid, Node CLI)
To browse the docs locally:
pnpm install
pnpm turbo dev --filter=docsThen open http://localhost:3000.
ditherkit/
├── packages/
│ ├── core/ # @ditherkit/core — pure algorithms
│ ├── react/ # @ditherkit/react — client component + Web Worker
│ └── next/ # @ditherkit/next — server component + ISR + Sharp
├── apps/
│ ├── docs/ # Fumadocs documentation site
│ ├── example-next/ # Next.js App Router flagship example
│ ├── example-next-pages/ # Next.js Pages Router mirror example
│ └── e2e/ # Playwright e2e test suite
├── plans/
│ ├── docs-site-plan.md # Design decisions for the docs site
│ └── docs-site-backlog.md # State of work, organised by phase
├── AGENTS.md # Instructions for AI coding agents
└── README.md # This file
This is a pnpm + Turborepo monorepo. Requires Node 22+ and pnpm 10+.
# Install dependencies for every workspace
pnpm install
# Build every package
pnpm turbo build
# Typecheck every workspace
pnpm turbo typecheck
# Run a dev server for the docs site (with live package rebuilds)
pnpm turbo dev --filter=docs
# Or the flagship example app
pnpm turbo dev --filter=example-next
# Run the Playwright e2e suite
pnpm turbo testEach package is built with tsdown, and the apps
use Next.js with Turbopack. Turbo's dependsOn: ["^build"] ensures
package source is built before any downstream app consumes it.
Pre-1.0. The core algorithms and the three packages are stable and
production-usable, but the documentation site and example apps are
still being built out. See plans/docs-site-backlog.md
for the current state of work.
What works today:
- All three dithering algorithms (Floyd-Steinberg, Atkinson, Threshold)
- Arbitrary color palettes of any length
- Brightness and contrast pre-processing
<DitheredImage />in any React app with shared-worker optimisation and layout-shift-free sizing<DitheredImageSSR />in Next.js with ISR caching, WebP output, and automatic cache key generation from webpack content hashes- Full e2e test coverage on Vite + Next.js App Router + Next.js Pages Router fixtures
What's in flight:
- Complete documentation site (content is written; deployment host hasn't been chosen)
- More example apps: Vite, Astro, Remix, Node CLI
- Interactive playground page with palette editor and URL state
- Consolidation of the e2e fixtures into the example apps
MIT © James Greenaway.