Skip to content

jvgomg/ditherkit

Repository files navigation

ditherkit

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/core directly.

Packages

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.

Quick start

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/.

Example apps

  • apps/example-next — Next.js App Router flagship. Demonstrates both kinds of src (imported StaticImageData and 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.

Documentation

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=docs

Then open http://localhost:3000.

Project structure

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

Development

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 test

Each 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.

Status

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

License

MIT © James Greenaway.

About

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages