Programmatic 1200 × 630 OG-image covers. Deterministic canvas renderers from physics, generative art, and cartographic tradition. No AI & no API calls.
Every renderer is a pure (ctx, W, H, SEED) → void function painting onto a 1200 × 630 canvas — the standard OG image size. The same slug always paints the same pixels. No clocks, no Math.random, no network. Drop the result into Next.js, Vite, CRA, or any other React app, or call the renderer directly from a Satori or node-canvas pipeline at build time.
npm install better-coversPeer dependency: react@>=18.
import { Cover, renderHoarfrost } from "better-covers";
export function Post() {
return (
<Cover
render={renderHoarfrost}
seed="my-post-slug"
title="A piece on cold weather"
subtitle="DLA descending from a top seed line"
/>
);
}Or call a renderer directly:
import { renderClifford, hashStr } from "better-covers";
const canvas = document.createElement("canvas");
canvas.width = 1200;
canvas.height = 630;
const ctx = canvas.getContext("2d")!;
renderClifford(ctx, 1200, 630, hashStr("any-string"));The full demo gallery is available as a React component:
import { Gallery } from "better-covers";
export default function GalleryPage() {
return <Gallery />;
}Full primary-source citations available in ATTRIBUTIONS.md and inside the doc comments of each renderer. Research prototypes and the curated HTML gallery in research/ and examples/research-gallery/.
Every renderer is deterministic in (W, H, SEED). Don't use Math.random, Date.now(), performance.now() or requestAnimationFrame, instead use mulberry32(seed) or hash2(x, y, seed) from better-covers/shared. Renders at different canvas sizes may differ, but the shape and the seed preserve identity; the Clifford attractor's parameters don't change with W.
That makes the covers usable at build time and from edge runtimes: serialize a slug, get back the same image every time.
The renderers work anywhere the 2D canvas API is available, i.e. Chromium, Firefox, Safari, Edge. On the server, pair them with node-canvas or @napi-rs/canvas. The ASCII renderer needs document.createElement('canvas'), which node-canvas does not polyfill out of the box; see docs/server-rendering.md for the workaround. On edge runtimes like Cloudflare Workers or Vercel Edge, pair the individual renderers with @vercel/og and an OffscreenCanvas polyfill as the <Cover /> React tree itself won't run on the edge.
See CONTRIBUTING.md for the full workflow. In short: open an issue before adding a new cover, every renderer needs a primary-source citation in its doc comment, and no verbatim copies of named artworks, i.e. you may implement a flow field, you may not reproduce a specific Fidenza output.
MIT, go crazy
The collection is dedicated to the people who looked carefully at one ordinary thing — frost on a window, a wake behind a stone, the way a pendulum loses energy — and described it precisely enough that the rest of us could draw it.