Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

✎ Slate — personal math whiteboard

One self-contained HTML file. No accounts, no cloud, no watermark, no build step.

A from-scratch infinite whiteboard in the spirit of Miro, built for personal use and math teaching: pressure ink with smart shape snapping, LaTeX math blocks, function plots, images, sticky notes — and full PDF import: drop in a PDF and every page lands on the canvas as a high-fidelity image — the design stays exactly as-is. Optionally add an editable text layer on top, and export it all back out as a PDF.

Current build: slate v0.8 — PDF import · single canvas · PDF export


Run it

Open slate.html in any modern browser. That's the whole install.

  • Boards auto-save to the browser (localStorage). Imported PDFs are stored as the original file once (compressed) and re-rendered on load — so even big multi-page PDFs stay well under the storage quota and survive a refresh.
  • Export → PDF downloads an A4 PDF — one page per imported PDF page, and any other content is split into readable A4 pages instead of being squashed onto a single page (same jsPDF engine as before).
  • Export → Import PDF (.pdf) — or just drag & drop a PDF — imports every page stacked vertically, pixel-for-pixel so nothing overlaps or changes. Each page is an image object you can move/scale/delete. Prefer the Import PDF + editable text option if you want real, double-click-editable text objects on top.
  • Works offline except three CDN loads: MathJax (math blocks), jsPDF (PDF export) and pdf.js (PDF import). Everything else — drawing, plots, PNG/JSON export — is fully offline.

Tip: if you host it in a sandboxed iframe (e.g. an embed that blocks downloads and storage), Slate detects that automatically: exports switch to an in-app overlay (right-click to save / Print → Save as PDF) and boards run in-memory with an amber "preview" indicator. In a normal tab everything is one-click.

Tools & shortcuts

Tool Key Tool Key
Select V Text T
Hand / pan H Sticky note N
Pen P Code block C
Highlighter L Math (LaTeX) M
Eraser E Image I (or paste / drag-drop)
Rectangle R Function plot X
Ellipse O Arrow A
  • PDF import: Export menu → Import PDF (.pdf) (exact design, image-only) or Import PDF + editable text (adds a text layer on top), or drag & drop a PDF onto the canvas. Pages stack vertically; imported PDFs export back out through Export → PDF with one exported page per imported page — annotations stay on their page.
  • Smart snap: draw a rough line / circle / rectangle and hold still for a beat before releasing — Slate replaces it with a clean shape. Release quickly to keep freehand.
  • Plots: press X, type e.g. x^2 - 3, sin(x)*x, 1/x. Safe hand-rolled expression compiler (no eval): sin cos tan asin acos atan sqrt abs ln log exp floor ceil sinh cosh tanh, constants pi e, powers ^, implicit multiplication (2x, 3sin(x)). Double-click a plot to edit.
  • Undo/redo Ctrl+Z / Ctrl+Y · duplicate Ctrl+D · copy/paste Ctrl+C/V · zoom Ctrl+wheel, F fit · grid G · snap-to-grid S · theme D · hide UI U.
  • Double-click any text / note / code / math / plot / imported PDF text to edit it.

Board format

Boards persist as JSON (also what Export → Backup gives you):

{
  "v": 3,
  "name": "Calculus notes",
  "objects": [ /* ... */ ],        // single canvas — no notebook pages
  "cam": { "x": 0, "y": 0, "z": 1 }
}

Object types: s stroke (pts: [[x,y,pressure]], hl: true = highlighter) · r rect · o ellipse · a arrow · t text · n note · c code · m math (latex) · i image (src data-URL, pdf: true on imported PDF pages) · p plot (expr, xmin, xmax).

Legacy backups migrate automatically: v1 ({ name, objects, cam }) loads as-is; v2 notebooks ({ pages: [...] }) flatten onto the single canvas, pages stacked with a gap so nothing overlaps.

Tests

Headless-browser end-to-end suites (Playwright, Chromium):

npm i playwright-core
npx playwright install chromium-headless-shell   # once

# serve the folder, then:
python3 -m http.server 8900 &
node tests/slate-e2e.mjs          http://localhost:8900/slate.html   # 45+ checks
node tests/slate-sandbox-test.mjs http://localhost:8900/slate.html   # blocked-storage env
node tests/slate-pdf-repro.mjs    http://localhost:8900/slate.html   # sandboxed-iframe exports

slate-e2e.mjs covers: drawing/shapes/text/math, select-move-undo-redo, localStorage persistence, PNG + PDF byte verification, v0.6 features (highlighter, image insert, plot compiler, smart snap), and v0.8 (single canvas, full PDF import image-only default + opt-in text layer, v1/v2 → v3 migration, paginated PDF export with one page per imported page). slate-v7-shot.mjs recreates the showcase screenshot.

Notes

  • Rendering: single <canvas>, variable-width filled-outline ink (perfect-freehand technique, hand-rolled), scene-snapshot fast path while drawing (O(1) per frame regardless of board size).
  • Math: MathJax tex-svg → SVG → cached bitmaps, so equations stay crisp at any zoom.
  • PDF import: pdf.js renders each page to a canvas (white-backed image object) so the design is preserved exactly. The opt-in text layer extracts runs into native t objects — position-approximate but fully editable — kept off by default because overlay text can never align perfectly with the raster.
  • Storage: boards save the original PDF bytes once (deflate-compressed), not one JPEG per page, and re-render pages after a reload — fixing the old "Storage full → PDF gone after refresh" failure.
  • Everything lives in slate.html — readable, commented, ~1,875 lines.

Releases

Packages

Contributors

Languages