All projects

DocuSlide

Turns a PDF, a set of notes or a plain prompt into a teachable slide deck. The interesting part is what happens when every model endpoint is down — it still produces a deck, and tells you it did.

)) }

1 / 2

What it is

DocuSlide takes unstructured educational input — a PDF, markdown notes, a curriculum outline, or a sentence describing what you want to teach — and produces a multi-layout slide deck with speaker notes, inline SVG diagrams and a presentation mode.

The client is a slide studio: switch layouts live, edit text inline, export to standalone HTML, JSON or PDF. The server coordinates generation against Gemini with schema-constrained output.

3 tiers of failure, 3 answers

Most demos of this kind work until the API doesn’t. Since a deck is usually needed at a specific time, an outage is not an inconvenience — it is a missed lecture. So the resilience design got more attention than the generation:

Tier 1 — model rotation. A pool of 3 candidate models, tried in order: a fast one for throughput, a stronger one for complex scientific or historical restructuring, and a stable alias as backstop.

Tier 2 — fast rotation on transient errors. Each call races a 25-second timeout. On a 503, a 429 or a connection timeout, the system does not retry the same model with backoff — it skips remaining retries and promotes the next model immediately. Retrying a model that is at capacity just queues behind the problem.

Tier 3 — deterministic offline synthesis. If every endpoint is unavailable, a local generator parses the prompt for sections, entities and bullet points, applies the requested theme and layout, and synthesises inline SVG diagrams with no model involved at all. The deck is flagged isResilientFallback: true with a visible note, so the user can present now and regenerate later.

That flag is the part I would defend hardest. A degraded output that is labelled is a product. A degraded output that is not is a trap.

Intent arbitration

Users describe what they want in one sentence, and that sentence usually carries 4 separate instructions: a theme (“cyberpunk”, “terracotta”, “pastel”), a tone (“witty”, “rigorous”, “storytelling”), a layout preference, and a slide count (“make it 8 slides”).

An intent extractor pulls each out, and a priority arbiter decides what wins when they conflict — an explicit slide count beats an inferred one, an explicit theme beats a tone-implied palette. Getting this wrong produces decks that almost match what was asked for, which is more annoying than clearly ignoring the request.

Stack

React 19, TypeScript, Tailwind on the client; Node/Express on the server; @google/genai with schema-constrained structured output. PDF, markdown and plain text ingestion. Exports as a standalone offline HTML bundle, JSON for persistence, or a print-ready multi-page layout.

What I learned

Retry logic should rotate, not repeat. Backing off against a model that is at capacity spends the user’s time waiting for a thing that is not coming. The next model in the pool is usually fine and always faster.

Label degraded output. The fallback decks are worse than the generated ones. Saying so — on the deck itself — is what makes them acceptable rather than suspicious.

Ambiguous prompts need an explicit precedence order. Once users can say anything in one line, the product’s job is deciding which part of that line to honour when the parts disagree. That is a design decision, and it should be written down rather than emerge from the order of if statements.