Universe

Hand-drawn pencil/chalk-style figures and annotations for Typst. Shapes are built as point lists in pure Typst, then a Rust → WASM engine (chalks-engine) perturbs them into sketchy, variable-width filled outlines — jitter, bowing, taper, multi-pass strokes, and two reliable fill patterns.

Gallery of primitives and fills Annotated mathematical equations Chalk-themed dark slide
All primitives and fill patterns Pin and annotate math content Chalk theme on dark backgrounds

Quick start

#import "@preview/chalks:0.1.0" as chalks

#chalks.sketch(200pt, 100pt,
  chalks.rect((10, 10), (90, 60), fill: "hachure"),
  chalks.circle((150, 40), 30, fill: "shade"),
  chalks.arrow((100, 40), (120, 40)),
)

The module import avoids shadowing Typst’s built-in line, rect, and ellipse functions. Import individual chalks functions explicitly when a flat local API is more convenient.

sketch(width, height, ..elements) lays out primitives (line, arrow, rect, ellipse, circle, polygon, region, brace, bracket, path, fn-curve) in a plain coordinate space and returns ordinary content — embeddable anywhere, no CeTZ dependency. Pass origin: "bottom-left" for y-up, math-convention plots.

Annotate content by name — a pin marks a spot, annotate draws a mark anchored to it, called after the pin(s) in flow order on the same page:

#import "@preview/chalks:0.1.0": annotate, pin

The key #pin("idea")[idea] deserves a ring.
#annotate(circle: "idea")

annotate also takes underline:, box:, and arrow: (from, to).

annotate places its mark in page coordinates, so call it — like the pins it references — directly in top-level page flow, not nested inside a grid/stack/table cell; those containers give place() their own local frame, which throws off annotate’s page-relative math (the mark can render clipped away or offset from its pin).

Style keys

Every shape/stroke/fill call accepts style overrides as named arguments (roughness: 1.5, fill: "shade", seed: 42, …); unset keys fall back to the active theme, then to these defaults:

Key Applies to Meaning Default
smoothness stroke + fill 0 = sharp polyline corners, 1 = fully flowing curve 0.7
roughness stroke + fill amplitude of jitter/bowing relative to size 1.0
width stroke + fill nominal stroke width (pt) 1.2
taper stroke pressure variation 0-1 (0 = uniform, 1 = strong taper at ends) 0.5
passes stroke number of overlapping strokes (1 = single, 2 = sketchy double) 1
pattern fill hachure | shade hachure
angle fill hachure/shade direction (deg) 45
spacing fill gap between doodle lines (pt) 4
color stroke + fill fill/stroke color #44464a
opacity stroke + fill overall opacity 100%

Seeds are derived deterministically from input geometry by default, so unchanged figures never re-roll between compiles.

For path, width is the full nominal stroke thickness (so a desired half-width/radius of 3 pt means width: 6), while smoothness controls how roundly the curve passes through its points. Chalks does not currently provide a fixed-radius corner-rounding parameter.

Themes

Document-wide presets, set with #chalks-theme(<preset>):

  • pencil — the default look (graphite gray, textured, tapered); the empty overlay.
  • ink — darker, single-pass, crisper.
  • chalk — light-on-dark, wider, softer; for dark backgrounds/slides.

Raw engine access

raw-stroke(points, closed:, style:, seed:) and raw-fill(boundaries, style:, seed:) call the engine directly on explicit point lists / boundary rings, bypassing the shape builders.

Rebuilding the engine

plugin/chalks_engine.wasm is a prebuilt artifact. Rebuild it with the pinned Rust toolchain (see rust-toolchain.toml at the repo root) via:

make plugin

run from the repo root (or make plugin inside chalks/, which delegates to the same recipe). Builds are byte-reproducible per platform, but rustc emits functions in a host-dependent order, so the committed artifact is always the x86_64 Linux build (what CI verifies); regenerate it from any host with make plugin-linux (requires Docker).

Development

From the repo root (needed so @preview/chalks:0.1.0 resolves for examples):

make test      # rebuild plugin, compile tests + manual, run error-message assertions
make examples  # compile chalks/examples/*.typ via @preview/chalks:0.1.0

See the manual source or compiled PDF for a rendered walkthrough of the API (every snippet shown is compiled, not just illustrative). Complete figures are available as the primitive gallery, an annotated equation, and a chalkboard theme example.