A canvas-based slide / presentation builder in React + TypeScript — Fabric.js editing, animated dynamic backgrounds, rich text via TipTap, PDF export. No backend.
This is a from-scratch attempt at a Keynote-style editor that runs entirely in the browser. The point is to learn what a polished slide tool actually requires once you stop hand-waving — selection state, undo / redo across heterogeneous object types, animated backgrounds that don't murder the framerate, sortable thumbnails, presenter mode, and a PDF exporter that doesn't lose fonts.
- Canvas editing — Fabric.js-backed slide canvas with text, shapes, images, charts, tables; drag, resize, rotate, snap, multi-select
- Rich text — TipTap editor with font family, colour, alignment, underline, lists, KaTeX math
- Dynamic backgrounds — 7-mode background system (crossed waves, bold ribbon, ridge layers, faceted, smooth wash, …) with 18 presets and per-slide configuration
- Animation — per-object enter / exit animations driven by Framer Motion + GSAP, sequenced via the Animate inspector
- Slide management — sortable thumbnails (DnD Kit), templates, layout chooser, theme chooser
- Presenter mode — full-screen playback with notes view, rehearsal timer
- PDF export — jsPDF with embedded fonts, one slide per page
- Document state — Immer-based store with undo / redo across all object mutations
- No persistence layer — everything lives in memory; reload loses the deck (intentional during exploration; would slot a
localStorageor file-handle backend in cleanly) - No collaboration / real-time sync
- No image-cropping UI; images are placed at native size
- Animation timing is per-slide, not synced across the deck
src/
├── shell/ # Top-level UI shell
│ ├── AppShell.tsx # Layout: toolbar / sidebar / canvas / inspector
│ ├── Toolbar/ # Top toolbar (Add Slide, Play, Insert, Share)
│ ├── Sidebar/ # Slide thumbnails + drag-to-reorder
│ ├── Inspector/ # Format / Animate / Layers / Document tabs
│ ├── ThemeChooser/ # Palette + font picker
│ ├── LayoutChooser/ # Per-slide layout templates
│ └── TemplateBuilder/ # Multi-slide template export
├── canvas/ # Slide editing surface
│ ├── CanvasManager.ts # Fabric.js wrapper, object lifecycle
│ ├── SlideCanvas.tsx # React mount point with zoom + pan
│ ├── interactions/ # Selection, drag, resize, snap
│ ├── objects/ # TextBox, Image, Shape, Chart, Table object classes
│ ├── shapes/ # Shape library (rounded rect, arrow, callout, …)
│ └── text/ # TipTap integration on canvas
├── animations/ # Object + background animation
│ └── backgrounds/ # Background renderer + 5 named modes + presets
├── slides/ # Slide-level features
│ ├── content/ # Slide content rendering
│ ├── management/ # CRUD, duplicate, reorder
│ ├── presenter/ # Full-screen playback
│ └── rehearsal/ # Rehearsal timer + speaker notes
├── store/ # Immer-backed document state, undo / redo
├── themes/ # Theme tokens + palette presets
└── types/
- Fabric.js for the canvas, not raw
<canvas>. Hit-testing, selection handles, and object hierarchy are the unsexy work of any editor; reaching for Fabric let me focus on what's interesting (animation, backgrounds, document model) instead of reimplementing transform handles for the third time. - Immer + a single store, no Redux. Slide-deck state is a tree, not a flat list. Immer's draft mutation lets reducers read like ordinary code while still giving structural sharing for cheap undo / redo. A handful of
produce(...)calls beat a Redux ecosystem. - Background renderer is its own subsystem. Each background mode is a small
(ctx, t, params) => voidrenderer driving arequestAnimationFrameloop on an offscreen canvas, composited under the slide. Adding a new mode is one file innew-renderers/. Presets are pure data. - TipTap on Fabric. Text objects host a TipTap editor inside a positioned
<div>overlay synced to the Fabric object. Rich editing without giving up canvas-based layout.
npm install
npm run devOpen the printed local URL. The first slide is editable on load.
React 18 · TypeScript · Vite · Fabric.js · TipTap · Framer Motion · GSAP · DnD Kit · Immer · jsPDF · KaTeX
