Create sketchy, hand-drawn style charts in the browser. Import CSV data, edit live, and export as PNG/SVG/JPEG.
Built with Next.js 15, roughjs, D3, Tailwind 4, and daisyUI 5. Everything runs client-side — there is no backend, and your data never leaves the browser.
- 3 chart types: Grouped Bar, Stacked Bar, Line
- CSV import: Drag-and-drop or click to upload
- Live editing: Edit data, labels, and titles directly on the page
- Hand-drawn style: Configurable roughness, fill styles, and re-rollable stroke randomness (seeded, so charts don't wiggle while you edit)
- Negative values: Diverging bars/stacks with a zero line
- Export: True vector SVG (font embedded), plus 2x PNG and JPEG, named after the chart title
- Auto-save: Data persists in LocalStorage across sessions
- Responsive: Works on desktop and mobile; long axis labels rotate, legends wrap
npm install
npm run devOpen http://localhost:3000.
npm run dev- Start development servernpm run build- Production build (Next.js + OpenNext worker bundle)npm run start- Start production servernpm run lint- Run ESLintnpm run typecheck- TypeScript type checknpm test- Run unit tests (Vitest)npm run generate-icons- Regenerate logo/favicon/OG assets fromscripts/generate-icons.mjsnpm run preview- Build and preview the Cloudflare Worker locally
Copy .env.example to .env and adjust as needed. There is one variable:
| Variable | Required | Purpose |
|---|---|---|
NEXT_PUBLIC_SITE_URL |
No | Absolute base URL of the deployed site, used to resolve Open Graph / Twitter card image URLs. Defaults to http://localhost:3000. |
NEXT_PUBLIC_* values are inlined at build time, so this must be set in the
build environment — not as a runtime worker variable.
The app deploys to Cloudflare Workers via
@opennextjs/cloudflare. Worker config
lives in wrangler.jsonc — change name to your own worker name.
npx wrangler deployOr connect the repo to Cloudflare Workers Builds and set
NEXT_PUBLIC_SITE_URL as a build variable.
It is a standard Next.js app otherwise, so Vercel, Netlify, or a self-hosted
next start all work without changes.
- Framework: Next.js 15 (App Router)
- Charts: roughjs + D3 (d3-scale, d3-array)
- Styling: Tailwind CSS 4 + daisyUI 5
- State: Zustand with LocalStorage persistence
- CSV: papaparse
- Export: native SVG serialization with embedded font; canvas rasterization for PNG/JPEG
- Font: xkcd Script (hand-drawn style)
- Tests: Vitest
All three chart types render through a single RoughChart component
(components/charts/RoughChart.tsx) that owns the SVG lifecycle, title/axis
chrome, and legend. Each chart type contributes only its mark-drawing logic in
components/charts/renderers/. Shared pure math (domains, stacking) lives in
components/charts/shared/scales.ts and is unit tested.
The source code is MIT licensed — see LICENSE.
The bundled font is not. public/fonts/xkcd-script.woff2 is
"xkcd Script", licensed
CC BY-NC 3.0 and based on the
handwriting of Randall Munroe. It may be used
non-commercially, with attribution. Chart exports embed the font, so exported
files carry those terms too.
If you want to use this project commercially, swap the font for a permissively licensed hand-drawn typeface — Comic Neue, Patrick Hand, and Shantell Sans are all under the SIL Open Font License. See NOTICE for details.