Ali Can Keskin2 downloadsSlide decks where each slide is an infinite canvas.
A fully vibe-coded Obsidian plugin I made for myself, built with contexture.
Canvas Deck makes slide decks in Obsidian where each slide is an infinite canvas. You draw on an Excalidraw canvas, mark a start frame, and save views on it. In present mode the deck steps through the slides and the views. A deck is a normal markdown note, so its titles and speaker notes stay searchable.

Taken in the browser test harness, so they use plain styles instead of your Obsidian theme.
| Present mode with the pen | Saved views on a 2,000-object slide |
|---|---|
![]() |
![]() |
| All slides and views (G) | Keys (?) |
![]() |
![]() |
The plugin is not in the community plugin store yet, and there is no BRAT setup. Install it by hand:
main.js, styles.css and manifest.json from the latest GitHub release.<vault>/.obsidian/plugins/canvas-deck/. The fonts come with main.js.Run New deck from the command palette or click the ribbon icon. New demo deck makes a deck with example slides.
#cdeck:<slideId> or #cdeck:<slideId>/<viewId> in Excalidraw's link box.| Command | What it does |
|---|---|
| New deck | Make an empty deck note |
| New demo deck | Make a deck with example slides |
| Present deck from current slide | Start present mode here |
| Present deck from start | Start present mode at slide 1 |
| Go to start frame | Fly back to the start frame |
| Save current view | Save what you see as a step |
| Insert vault image | Pick an image from the vault |
| Insert note | Pick a note to embed live |
| Insert code card | Add a live HTML/JS card |
| Edit code card | Edit the selected card's source |
| Insert web embed | Embed a web page by URL |
| Link selection to slide or view | Make the selection a link |
| Save slide as template | Save the current slide to the template folder |
| New slide from template | Add a slide from a template |
| Export deck to PDF | Save <deck>.pdf next to the note |
| Export deck to HTML | Save <deck>.html next to the note |
| Open deck as markdown | Show the raw note |
| Open note as deck | Go back to the deck view |
| Key | Action |
|---|---|
| → ↓ Space Enter PageDown | Next view or slide |
| ← ↑ Backspace PageUp | Back |
| Home | Start frame of this slide |
| N | Speaker notes (the current view's notes if it has some) |
| P | Pen |
| L | Laser pointer |
| K | Keep the ink on the slide (otherwise it goes when you leave the slide) |
| G | Grid of all slides and views, to jump to |
| E | Edit the slide with the full tools (Esc to stop) |
| ? | Show all keys |
| Esc | Close the grid or tool first, then exit |
Drag and scroll to move freely while presenting.
<deck>.html steps through the same start frames and views. Keys: → ↓ Space Enter PageDown next, ← ↑ Backspace PageUp back, Home start frame, N notes, F full screen. A click or tap on the right or left half also steps. Each page is a picture, so there is no free pan or zoom. Note embeds (as they were at export), code cards and web pages show on top of it. Web pages need the network. After a click inside an embed, click the slide once so the keys work again.
A deck is a markdown note with canvas-deck: 1 in its frontmatter. Each slide is a ## heading, then its speaker notes as plain text, then a %%cdeck <slideId> block that holds the canvas data as one line of JSON:
---
canvas-deck: 1
---
## First slide
Speaker notes for this slide.
%%cdeck a1b2c3
{"version":1,"frame":{...},"views":[...],"elements":[...],"images":{...}}
%%
The %% comment hides the data in reading view. Titles and notes stay searchable. View notes live inside the slide data.
npm ci
npm run build # main.js, styles.css and fonts/
npm run dev # watch build
npm test # unit tests (vitest)
Browser tests use Playwright against a test page that runs the canvas outside Obsidian:
npm run harness # open the test page at http://localhost:5199
npm run test:e2e # run the Playwright tests
If Playwright's own browser is not installed, set CHROMIUM_PATH to a Chromium or Chrome binary.
To update the README screenshots in docs/screens, run SHOTS=1 npx playwright test readme-shots.
Planning docs written while building it are in .claude/visions, .claude/specs and .claude/plans.
app.emulateMobile(true) in the console and repeat.To release, bump version in manifest.json, package.json and versions.json, and merge to main. The Release workflow creates the GitHub release for that version.
main.js is about 9 MB, mostly Excalidraw. Obsidian Sync Standard does not sync files over 5 MB, so install the plugin on each device.MIT. See LICENSE.