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. |
Pre-release: the packages aren't published to npm yet, so the install commands below won't work until they are. To try ditherkit now, clone this repo and follow Development.
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.
