How to build a Gamma alternative

How to build a Gamma alternative turns one markdown outline into themed slides you present in the browser, and keeps that outline as the only copy of the deck.

  • guide
  • vibe-coding
  • gamma

How to build a Gamma alternative is one markdown outline, a theme, and a keyboard that walks the slides. Gamma is that product. The catalog line is “Your outline is already a slide deck.”

The card lists $10–$20 a person a month. That is $120–$240 a year for every person. The verdict is “Markdown in, slides out”. The first estimate is 8 hours: markdown to slides, themes, and keyboard presenting. The project name on the card is Slidenote.

The Canva post is the cousin that points here when the job is slides rather than a social square. A framed screenshot is the screenshot frame guide and the Shots.so post. Shots.so is $10–$20 a month, about 2 hours. A freeform canvas is Figma, $16–$55 a person a month. The sample deck in this guide is the invoice from the FreshBooks guide.

How to build a Gamma alternative from one outline

The Gamma page splits the work into three rows.

The first row is 8 hours. You paste an outline. Headings become slides. You pick one of three themes. You present in the browser with the keyboard. The file you saved is still the markdown.

The second row is 3 days, 24 hours. Speaker notes in a second window, a read-only link you can revoke, and a PDF with one slide per page. The third row is a month and up, 160 hours. AI generation, live co-editing, and animations.

If you tell an agent to “clone Gamma”, it aims at the model and the cursors. Paste the prompts below and keep the six-slide invoice talk on the first row.

What the 8-hour build does

You create a deck called Bill the client. You paste six headings. The rail shows six slides, in that order, every time you open it. You switch from Paper to Ink. The words stay. A slide that is too tall gets a badge, and the badge clears when you cut a paragraph.

You press present. The right arrow moves one slide. Escape returns to the editor. The markdown is unchanged. A heading inside a code fence stays code. A :::notes block stays in the file and stays off the audience slide. Two browser tabs cannot silently overwrite each other. The save you opened earlier is refused when the deck has moved on.

What it leaves out

No generated prose. No second cursor. No transitions. No template shop. No share link and no PDF yet. Those two are the 24-hour row, and the prompts stop before them. Notes are stored now so the audience never sees them, and the window that shows notes beside the next slide waits.

The same v1 also replaces Beautiful.ai, Pitch, and Tome if what you wanted was an outline that presents. The card names them and does not give them prices.

8 HOURS · SHIP Markdown becomes slides Paper, Ink, and Lime Keyboard present 24 HOURS · NEXT Notes in a second window A link you can revoke One slide per PDF page 160 HOURS · SKIP AI writing the outline Live cursors Animations and a theme shop
Hours are the three estimates on the Gamma card. The 8-hour box is the build in this guide.

Where the outline lives

One owner, one browser for the editor, and later a second browser that only has a token. Node.js and Astro render the list and the present page. Use TanStack Start if React is your weekday language. The stack post is which tab you copy. The card lists D1, so the deck row lives there. There is no object storage in this version. A PDF is a print of HTML, not a file you upload.

The deck row holds the title, the markdown, a theme name, and updatedAt. Slides are not rows. The splitter reads the markdown and returns an ordered list. The editor runs that splitter in the browser so the rail keeps up while you type. Save sends the markdown. The server splits it once before storing, and refuses the save if the split throws. On the next load, the server splits again for the present page. If the rail and the present page disagree, the server split is the one you fix the client to match. One set of rules, two calls.

Present is server-rendered HTML for every slide, stacked. A short script shows one at a time and listens for keys. With the script off, you can still read the talk by scrolling. The deck does not depend on the script to exist.

Save includes the updatedAt you loaded. If the row is newer, the server writes nothing and the editor reloads. Two tabs are the whole concurrency story. Live cursors are the 160-hour row. When D1 is down, the last good markdown is still what you had, and a failed save does not clear the textarea.

Sanitize on the way out to HTML. Strip scripts, iframes, and javascript: links. A fenced code block is text, including a line that looks like a heading. The notes fence is removed from the audience HTML and kept in the markdown.

Nothing else is called. No model, no font CDN you do not already trust, no email. The 24-hour share page is the same HTML with the editor routes absent. A bad token renders one not-available page. It does not tell you whether the deck was revoked or never existed.

EDITOR BROWSER Markdown, rail, theme Keys show one slide ASTRO ON THE REQUEST Split, then sanitize Stale updatedAt is refused Present is the saved source D1 · ONE DECK ROW Markdown is the copy Theme name, updatedAt OUTSIDE THIS VERSION Share token and revoke A stored PDF A stored slide table AI text and live cursors Animations
The row is the outline. Slides are a split you can run again. A failed save leaves that outline where it was.

What a deck actually stores

The card lists Deck, Slide, Theme, and ShareLink. Store Deck. Theme is a name on that row, not a document. Slide is the splitter’s output. ShareLink is a token you add on the 24-hour row, pointed at one deck, with a revoked time.

A deck is Bill the client. Title, markdown, theme paper or ink or lime, updatedAt. Duplicate copies the markdown and the theme into a new row with a new time. It does not copy a share token, because v1 has none. Delete removes the row. There is no trash.

The six headings in the sample are the whole talk:

  1. Bill the client. Four posts, one number.
  2. The line. Quantity 4, unit 45000 cents.
  3. The lock. Send assigns 2026-014. Total 193050 cents.
  4. The link. Ada opens a token.
  5. The payment. Record 193050 cents. The banner says paid.
  6. What you skip. Banks, payroll, and a ledger.

A slide has an order, an optional title, body HTML, and optional notes. You do not insert these. You get them by splitting. Empty markdown is one empty slide, so present has somewhere to open. Prose before the first heading is an untitled opening slide, and the six headings then follow it. That opening slide is why a pasted intro does not vanish.

A thematic break ends the current slide. Two breaks in a row insert one blank slide. A break that sits directly in front of a heading does not insert a blank. The heading is the next slide. A heading inside a code fence is not a break.

# The lock

~~~
# this is code
~~~

The number stays on this slide.

That fixture is one slide. The hash line is code.

A theme changes type, color, padding, and the slide’s aspect. It does not change the markdown, the slide count, or the order. Paper is the light page, Ink is the dark page, Lime is the high-contrast page. Unknown names are refused and the stored theme stays.

Notes are a fence the audience HTML drops:

:::notes
Say 2026-014 out loud.
:::

The words remain in the markdown. Present mode does not show them. The 24-hour speaker window is where they appear, next to the upcoming slide.

A share link, later, is a random token of at least 128 bits, one deck, read-only, revocable. Reissue makes a new token. The old one uses the same not-available page as a random string.

DECK ROW Bill the client · theme paper Markdown plus updatedAt SPLIT, DO NOT STORE 1 Bill the client · 2 The line 3 The lock · 4 The link 5 The payment · 6 What you skip NOTES FENCE In the file, off the slide LATER A slide table A share token on this deck
Six headings, six slides, zero slide rows. The theme is a name on the deck.

The screens you ship

The deck list, the editor, and present. Share and the speaker window are drawn so the next row has a shape, and the first prompts do not build them.

Deck list

Each row is the title, the theme name, the derived slide count, and the last edited time. Actions are open, present, duplicate, and delete. An empty account says “No decks yet” and has one create button. Create opens the editor with empty markdown, which is one empty slide, theme Paper.

WIREFRAME · DECKS Bill the client Paper · 6 slides · edited today Present · Duplicate · Delete NEW DECK Count comes from the split
The count is the splitter's length. Duplicate makes a new row with the same markdown.

Editor

A markdown pane, a slide rail, and one preview of the selected slide. The rail numbers the six titles and puts an overflow badge on any slide whose content is taller than the theme. The theme picker is three buttons. The save line shows the updatedAt you loaded, or “reload, this deck changed” when the stale check fails.

Jumping in the rail scrolls the preview. It does not rewrite the markdown. Overflow measures after images load. Until then the badge reads “measuring”. A theme change measures every slide again, because padding and type changed.

WIREFRAME · EDITOR 1 Bill 2 Line 3 Lock 4 Link 5 Payment 6 Skip ! too tall The lock 2026-014 193050 cents Notes hidden here PAPER INK LIME Markdown pane beside this preview Saved, or reload if the deck changed
The rail is a view of the markdown. The badge is height, not a second document.

Present

One slide fills the window. A corner reads “3 of 6”. Arrow keys and space move. Escape leaves. The notes fence is absent from the pixels. There is no next-slide thumbnail yet. That thumbnail is the speaker window.

WIREFRAME · PRESENT The lock 2026-014 · 193050 cents 3 of 6 · arrows · space · esc Audience slide hides the notes Scroll still works with script off
One slide at a time when the script runs. The same HTML is a scroll if it does not.

Screens that wait

The speaker window shows the current slide, the next slide, the notes fence, and a timer. Either window can advance. Closing the speaker window leaves the audience window where it was. Reopening it rejoins that slide. A blackout key blanks the audience window and keeps the index.

The share screen is the present HTML for visitors, with no editor and no list. The owner has a create, copy, and revoke control on the deck. The PDF is print CSS on that same HTML: one slide per page, notes omitted, backgrounds kept.

From six headings to slide 6

You paste the six headings and save. The list says 6. You open present, land on Bill the client, and press the right arrow five times. The last slide is What you skip. Escape. The editor text matches what you pasted.

You add a code fence whose first line is # this is code under The lock. The rail still says 6. You add a notes fence on The lock. Present on that slide shows the number and the total, and the words “Say 2026-014 out loud” are absent. They are still in the textarea.

You open a second tab and save a one-word edit from the first tab. The second tab’s save is refused. Reload brings the one-word edit in. You did not merge two outlines. You kept one.

You set the theme to Ink. The six titles remain. Slide 6, stuffed with three extra paragraphs, shows the overflow badge. Delete the extra paragraphs. The badge clears. Switch to Lime. If the padding is tighter and the text fits, the badge stays clear. If it does not fit, the badge returns. The markdown is the same either way.

A script tag in the outline does not run in the preview. The text can remain as text, or be stripped. Pick strip, and keep the rest of the slide.

PASTE SIX HEADINGS DOES THE SERVER SPLIT? NO Keep the textarea YES Store markdown PRESENT 1 OF 6 SECOND TAB IS STALE Refuse, then reload NOTES STAY IN THE FILE Share link and PDF come next
Split, store, present. A stale tab reloads. The notes fence never becomes a slide.

Prompt 1. A deck row and a list

Copy the Astro prompt from Build your own Gamma. Paste this above it.

Ship the 8-hour row only. Project name: Slidenote.
Node.js and Astro. The deck row is in D1.
TanStack Start is the same screens and the same rules,
with server functions and a SQL database.
The live rail and the present keys are the client bits.
Everything else is server-rendered.

One owner. A deck stores title, markdown, theme name,
and updatedAt. Theme is paper, ink, or lime.
Default paper. Empty markdown is allowed.

List shows title, theme, derived slide count, edited time,
open, present, duplicate, and delete.
Duplicate copies markdown and theme into a new row.
Delete removes the row.

Save sends the markdown and the updatedAt you loaded.
If the row is newer, write nothing and tell the editor
to reload.
Do not store slides. Do not call a model.
No share link, no PDF file, no cursors, no animations.

When the plan lists routes and tables, stop.

What to check after the list

Create Bill the client. Reload. It is there, theme Paper, edited time set. Duplicate. Two rows. Delete the copy. One row. Save from tab A, then save from tab B without reloading. Tab B gets the refusal and tab A’s markdown is still the row. A logged-out window does not see the deck. The table is decks. There is no slides table.

Prompt 2. The splitter

Add the splitter used by the editor rail and by save.

Rules, in order, on the parsed document:
An h1 starts a slide.
A thematic break ends the current slide.
Two breaks in a row insert one blank slide.
A break directly before an h1 does not insert a blank.
Prose before the first heading is an untitled opening slide.
An empty document is one empty slide.
A heading inside a code fence is code, not a boundary.
A :::notes fence is notes on the current slide.
Notes are stored in the markdown and omitted
from audience HTML.

Sanitize audience HTML.
Strip scripts, iframes, and javascript: links.
The code fence fixture with a hash line is one slide.

The sample outline has these h1 titles and no leading prose:
Bill the client, The line, The lock, The link,
The payment, What you skip.
That outline is 6 slides, same order, every run.

The client rail may split for typing.
Save splits on the server and stores markdown only.
If the rail and a reload disagree, fix the client.

Done when the sample is 6, the code-fence fixture is 1,
two breaks make a blank slide, and notes text
is in the textarea and absent from the preview.

What to check after the split

Paste the six titles. The rail shows those six, in order. Reload. Still six. Wrap # this is code in a fence under The lock. Still six, and the preview shows the hash line as code. Add the notes fence. The preview of The lock shows 2026-014 if you wrote it, and hides “Say 2026-014 out loud”. Put a sentence above the first heading. The rail gains an untitled first slide and the six titles follow. Clear the editor. One empty slide, and present can open it.

Prompt 3. Three themes and the overflow badge

Add themes and overflow on Slidenote.

paper, ink, and lime change type, color, padding,
and slide aspect. They do not change markdown,
slide count, or order.
An unknown theme is refused.

Measure each slide in the theme's box.
If the content is taller than the box, badge that rail item.
Images badge only after they load. Until then show measuring.
A theme change remeasures every slide.
Cutting text clears the badge when the slide fits.

Done when the sample stays 6 slides on all three themes,
and a too-tall What you skip shows a badge on Ink
that clears after the extra paragraphs go.

What to check after themes

Switch Paper, Ink, Lime. The stored markdown string is identical. The list theme name matches the buttons. Slide count stays 6. Paste three extra paragraphs on What you skip. The badge appears. Delete them. The badge is gone. Add an image and confirm the badge waits until the image has a height. A posted theme of brand leaves the deck on its previous theme.

Prompt 4. Present from the keyboard

Add present on Slidenote.

Render every slide from the saved markdown on the server.
A small script shows one slide. Arrow keys and space move.
Escape returns to the editor.
The corner shows the index, like 3 of 6.
Notes are omitted.
With the script disabled, the slides still read in order
as a scroll.

Do not open a second window yet.
Do not add a share URL.
Present does not write.

Done when the sample starts on slide 1,
five right arrows land on What you skip,
and the markdown is unchanged.

What to check after present

Open present. The first heading is Bill the client. Arrow to The lock. The corner says 3 of 6 if The lock is third. The notes sentence is not on screen. Escape. The textarea is intact. Turn the script off and load present. You can read all six by scrolling, in order. A second present tab does not change updatedAt.

Failures the agent ships anyway

A slide table

The list count is right on day one. You edit The lock and the table still has the old sentence, or it has seven rows because a code fence was split. The deck row’s markdown is the talk. If you find slide rows, stop writing them and derive the rail on read.

Two splitters

The rail shows 6 and present shows 7. The client is splitting on raw lines, so a hash inside a fence opens a slide. Run the server rules on save, and make the rail call the same rules. The code-fence fixture is the check. One slide, or the client is wrong.

Notes on the wall

Present shows “Say 2026-014 out loud” under the total. The fence was treated as a paragraph. Drop that block from audience HTML. Leave it in the textarea so the speaker window has something to read next week.

Last write wins with no time

Tab B saves an older outline over tab A’s edit. updatedAt moved even though tab B never loaded the new sentence. Require the time you loaded. A mismatch writes zero bytes of markdown.

A model button

The editor grows a “generate” control. The 160-hour row is the one that writes prose for you. This row stores the words you pasted. Remove the button.

You will add this on the next row. If a token URL shows the markdown pane or the deck list, the token is an owner session. The visitor HTML is the present scroll, and revoke turns it into the same not-available page as a bad token.

The 24-hour row, after the talk presents

Present the six slides once, from the file you actually saved. Then add the speaker window, the link, and the PDF.

The speaker window is a second browser window. It shows the current slide, the next one, the notes, and a timer. One side owns the index. Arrow keys in either window move both. Closing the speaker window leaves the audience on the current slide. Opening it again rejoins that index. Blackout blanks the audience window only.

The link is a token on the deck. Create, copy, revoke, reissue. Visitors get present HTML and no editor. Wrong, missing, and revoked tokens share one not-available page. Rate-limit the lookup. Do not put the token in logs. Log the deck id and created or revoked.

The PDF is print CSS on the audience HTML. Page size matches the theme aspect. One slide per page. Notes, the rail, and the key hints are hidden. Backgrounds print. Six slides export as six pages. A twelve-slide deck exports as twelve. You do not store the file.

The scope post is the three-row habit. The prompt post is the extra line that says do not add a model.

Deploy, then present the six

Slidenote wants Astro and D1. Put it on a hostname you already own, slides.flaviocopes.com. The hosting post is the adapter and the domain. Take a backup of D1 before you delete a paid account. The backup is the markdown.

Log a save with the deck id, the byte length, and whether it was refused as stale. Leave the markdown out of the log. A talk can be private. Log a theme change as the deck id and the theme name. When you add share, log created and revoked without the token.

Then use it. Paste a real outline, six headings is enough. Present it. Confirm the rail, the present order, and the textarea still match.

Canva is $15–$30 a person a month, about 3 days, when the job is a template you reuse. Figma is the canvas. Shots.so is the framed picture. Slidenote is the outline.

What to skip on purpose

Leave AI generation, live co-editing, animations, and a template marketplace. Leave a stored PDF. Leave a slide table that can drift from the headings.

If the rent is “dress this screenshot”, build the frame tool. If the rent is “draw the boxes”, build the canvas. If the rent is “I already wrote the headings”, build this.

Start now

Go to Build your own Gamma. Copy the Astro prompt, or TanStack Start if that is what you write.

Tell the agent the first estimate. Markdown to slides, themes, keyboard presenting. Eight hours. The markdown is the only copy of the deck.

Paste the six headings. Present them. Reload, and read the same outline.