rough

A lightweight JavaScript library for drawing sketchy, hand-drawn-style graphics on Canvas and SVG.

Library
npm
v4.6.6
21,151stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
43/100Fair
Development Activity0
Maintenance20
Community52
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
71/100Good
Architecture78
Code Quality55
Innovation82
Learning Curve70

Rough.js is a small (under 9 kB) graphics library that renders shapes in a sketchy, hand-drawn style instead of the crisp, precise lines browsers draw by default. It defines primitives for lines, curves, arcs, polygons, circles, ellipses, and arbitrary SVG paths, and it can target either the HTML5 Canvas API or an SVG document, producing a <g> node tree in the SVG case and drawing directly to a 2D context in the Canvas case.

Every shape is generated twice — once for the outline and once for the interior fill — using a seeded pseudo-random offset so repeated renders of the same shape look hand-sketched rather than identical on every frame. Fill patterns include hachure (angled parallel lines, the default), solid, zigzag, cross-hatch, dots, dashed, and zigzag-line, all tunable through roughness, bowing, and curve-fitting options. The library ships as ESM, CommonJS, and an IIFE browser bundle, has zero runtime dependencies beyond a handful of small geometry helpers, and is the rendering engine behind tools like Excalidraw and diagrams.net for their whiteboard aesthetic.

What You Get

  • A RoughCanvas and RoughSVG API with matching methods (line, rectangle, ellipse, circle, linearPath, polygon, arc, curve, path) so the same drawing code targets either renderer
  • A RoughGenerator that produces reusable Drawable shape descriptions independent of any renderer, useful for pre-computing shapes or serializing them
  • Seven fill styles (hachure, solid, zigzag, cross-hatch, dots, dashed, zigzag-line) with per-shape control over fill weight, angle, and gap
  • Sketch-style tuning via roughness, bowing, curveFitting, and curveTightness options, plus a seed option for deterministic, reproducible output
  • SVG path string support (rc.path(...)), including arc-to-bezier conversion adapted from Mozilla’s SVG implementation notes

Common Use Cases

  • Giving whiteboard or diagramming tools (flowcharts, wireframes, mind maps) a hand-sketched look instead of sterile vector lines
  • Rendering hand-drawn-style charts, annotations, or illustrations embedded in a web app
  • Building sketch-style UI mockup or prototyping tools where the visual language should read as a draft, not a finished design
  • Adding a playful or informal visual treatment to data visualizations, presentations, or marketing graphics rendered client-side

Under The Hood

Architecture Rough.js separates shape description from shape rendering: RoughGenerator (src/generator.ts) computes a renderer-agnostic Drawable/OpSet structure for each primitive, and RoughCanvas (src/canvas.ts) and RoughSVG (src/svg.ts) each consume that same structure to draw to a 2D canvas context or build an SVG <g> node tree, respectively — so adding a new shape only requires generator logic, not renderer-specific duplication. Fill rendering is further factored behind a RenderHelper interface (src/fillers/filler-interface.ts) with one module per fill style (hachure, solid, zigzag, cross-hatch, dots, dashed, zigzag-line), and SVG path input is normalized through the external path-data-parser package before being re-walked into sketchy strokes.

Tech Stack The library is written in TypeScript and compiled with tsc, then bundled by Rollup into three targets — an IIFE browser build, an ESM build, and a CommonJS build — with rollup-plugin-terser for minification. Runtime dependencies are minimal and narrowly scoped: hachure-fill, path-data-parser, points-on-curve, and points-on-path handle geometry concerns that would otherwise be hand-rolled. There is no application framework involved — the library talks directly to the browser’s Canvas 2D and SVG DOM APIs.

Code Quality There is no automated test suite: the package.json test script is a stub (echo "Error: no test specified" && exit 1), and the only verification artifacts are manual visual-tests/canvas and visual-tests/svg HTML pages meant for eyeballing rendered output rather than asserting behavior in CI — no CI workflow file exists in the repo. Offsetting that, the codebase is fully typed TypeScript with an .eslintrc.json enforcing @typescript-eslint rules (no unused vars, consistent quotes/semicolons, eqeqeq), and the core modules (generator.ts, renderer.ts, canvas.ts, svg.ts) are small, single-purpose files with narrow public surfaces.

What Makes It Unique The defining technical choice is deterministic, seeded randomness applied to otherwise-precise vector geometry: every stroke is drawn as a double line with per-point random offsets derived from a seedable PRNG, so the same shape and seed always reproduce an identical “hand-drawn” result while an unset seed yields fresh sketchiness each render. Combining that with a single generator that targets both Canvas and SVG through the same primitive API is uncommon among small graphics libraries, and it’s this combination that made Rough.js the rendering engine underneath tools like Excalidraw and diagrams.net rather than a one-off visual gimmick.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search