ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
Illustrated, narrated books with a reading companion you can interrupt anytime: built for learners and readers who find it hard to focus.
EducationTypeScript · CSSGitHub @dagordaniOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Illustrated, narrated books with a reading buddy ("Page") you can interrupt at any time. It's built for people who find it hard to focus on a page and for second-language readers.
The demo book is chapters 1–2 of Alice's Adventures in Wonderland (public domain, from Project Gutenberg): 28 scenes, about 43 minutes of narration per voice across both versions.
The book is split into two phases:
1. Offline build (scripts/build-book.ts, run once on a laptop). Everything about the book is generated ahead of time and committed under public/books/<id>/:
simple: a Simple-English rewrite, paragraph-for-paragraphsummary: a one-line summaryglossary: 3–6 tricky wordsimagePrompt: a description of one key momentffmpeg.book.json, which embeds the default voice's timings. Each extra voice gets tracks/<voice>.json plus audio/<voice>/*.mp3.2. Runtime (Next.js on Vercel). The page loads static assets. Only the interactive parts call OpenAI, through API routes:
| Route | Purpose |
|---|---|
POST /api/ask | The tutor. It receives the current scene and what was just heard, crossing scene boundaries if needed, plus the story-so-far summaries and recent chat. It returns {reply, action}, where action is one of: resume, reread_sentence, reread_scene, goto_scene, switch_simple, switch_original, stay_paused. One fast gpt-4.1-mini call with a strict JSON schema (see trade-offs). |
GET /api/tts?text&voice | Speaks tutor replies. The <audio> element plays it directly, so the voice starts as soon as the first bytes arrive. POST is also accepted. |
POST /api/transcribe | Push-to-talk audio → text. |
POST /api/define | Tap-a-word definition, example sentence and translation. |
The client player (components/Player.tsx) is a small state machine:
idle → playing ⇄ paused → listening → thinking → speaking → (do the tutor's action) → playing…
Safeguards on the tutor's voice:
All models come from one vendor (OpenAI) and can be changed with environment variables.
| Job | When | Model (default) | Why this model |
|---|---|---|---|
| Scene enrichment: simple rewrite, summary, glossary, image prompt | Build | gpt-4.1-mini (OPENAI_MODEL) | Cheap and fast, strong at following instructions, and supports strict JSON-schema output. Writing at an A2–B1 level doesn't need a frontier model. |
| Illustrations | Build | gpt-image-2 (OPENAI_IMAGE_MODEL, script default gpt-image-1) | Best character consistency and painterly style in our tests. Slow (about 1 min per image), which is fine offline. |
| Narration | Build | gpt-4o-mini-tts, voices fable / coral / ash | Takes style instructions ("warm storyteller, give characters a little voice") and costs far less than premium TTS. |
| Word timing alignment | Build | whisper-1 | The only OpenAI transcription model that returns word-level timestamps. |
| Tutor ("Page") | Runtime | gpt-4.1-mini with chat completions (OPENAI_MODEL) | Low latency matters most: the listener has paused mid-story. Strict JSON output with a fixed action list keeps answers short and on-script. No reasoning model needed for vocabulary and plot questions. |
| Tutor voice | Runtime | gpt-4o-mini-tts, voice chosen by the listener (default coral) | Streams; the first audio arrives in about 1 s. |
| Mic input | Runtime | gpt-4o-mini-transcribe (OPENAI_STT_MODEL) | More accurate than Whisper for short, noisy questions. No timestamps needed. |
| Tap-a-word | Runtime | gpt-4.1-mini | One small JSON call. The result is cached per word and language in the browser. |
These are measured on the demo book (28 scenes, about 4.2k words of original text and 3.4k words of Simple English). Costs are approximate list prices at build time; check OpenAI's pricing page before relying on them.
| Asset | Volume | Wall-clock time | Approx. cost |
|---|---|---|---|
| Scene enrichment (LLM) | 28 calls | about 1 min (sequential) | < $0.10 |
Illustrations (gpt-image-2, 1536×1024, medium) | 28 images (+2 tests) | about 15–20 min (4 at a time, about 1 min each) | about $2–3 |
| Narration, one voice, both versions | about 43 min of audio, 56 files | about 8–10 min | about $0.65 |
Word timings (whisper-1, including retries) | about 43 min per voice | included above | about $0.30 per voice |
| First full build (1 voice) | about 25 min | about $3–4 | |
| Each extra narrator voice | about 46 min of audio | about 10–12 min | about $1–1.5 |
Disk footprint:
Rule of thumb: a full ~300-page novel is about 25× this sample. That's roughly 700 scenes, about $50–75 of images, and about $15–20 of audio per voice. Image generation dominates both time and cost.
| Action | Latency (measured) | Approx. cost |
|---|---|---|
Tutor answer (gpt-4.1-mini) | 1.3–2 s to text | < $0.001 |
| Total time from asking to hearing Page's voice | about 2.4 s | |
| Tutor voice (streamed) | about 1 s to first audio | about $0.001–0.003 |
| Voice question → text | about 0.5–1 s | < $0.001 |
| Tap-a-word definition | about 1 s, then instant from cache | < $0.001 |
Narration playback is free at runtime because it's pre-generated.
app/
page.tsx Landing page
read/[book]/page.tsx Reader page (static, one per book in lib/books.ts)
api/ask/route.ts Tutor: context assembly → LLM → {reply, action}
api/tts/route.ts Streaming TTS (voice allowlist)
api/transcribe/route.ts Mic audio → text
api/define/route.ts Tap-a-word dictionary
layout.tsx, globals.css Fonts (Literata + Atkinson Hyperlegible), theme, pan/zoom + caption animations
components/
Player.tsx State machine: narration, tutor, actions, scene-boundary logic, voices, persistence
Stage.tsx Illustration with Ken Burns pan/zoom, crossfade, chapter title card
Transcript.tsx Karaoke read-along, sentence ▸ markers, Cinema captions (chunked)
TutorPanel.tsx Chat, quick-ask chips, mic + text input, glossary
WordPopover.tsx Definition / translation / "Read from here"
icons.tsx
lib/
book.ts Types + timing helpers (tokenAt, sentenceRange, captionRange, …), voice list
books.ts Book registry (imports book.json)
openai.ts Client + model/voice env defaults
scripts/
build-book.ts Offline generator (text → scenes → images, audio, timings)
public/books/alice/
book.json Scenes, text, glossary, default-voice timings
tracks/<voice>.json Timings for extra narrator voices
img/*.webp Illustrations + thumbnails
audio/[<voice>/]*.mp3 Narration (default voice at top level)
.cache/ Build cache (LLM outputs, raw word timings); git-ignored
The app chains text LLM → image model → TTS → speech-to-text alignment rather than asking one multimodal model to "turn this chapter into an illustrated audiobook".
Why chaining fits:
What chaining costs:
Realtime speech-to-speech models (one model listening and talking) would make Page feel more conversational: barge-in and lower latency. But it's harder to keep a voice model grounded in exact book state and to return structured actions reliably. It's the natural next step for the tutor only (see the roadmap).
gpt-4.1-mini):
needs_research, and play a spoken filler ("Good question, let me check…") while it works.Next.js + Vercel, single app: no separate backend, queue or database. Deploy is git push.
One vendor (OpenAI): one key and one bill, the simplest possible demo. Alternatives may be better per stage:
The model defaults in lib/openai.ts are a single point to change.
Browser state only: reading position, voice and language live in localStorage. There are no accounts, and the chat history is lost on reload.
/api/ask; the voice has watchdogs but the text answer doesn't.The API routes are public and unauthenticated, and they spend money on your OpenAI key.
| Risk | Where | Current mitigation | Next step |
|---|---|---|---|
Cost abuse / denial of wallet. Anyone can script calls to /api/ask, /api/tts, /api/transcribe, /api/define. | All routes | Input length caps (question 1k chars, TTS 1.2k, audio 10 MB, 30 s of recording). A spending limit on the OpenAI dashboard. Each call is cheap (fractions of a cent), but nothing limits how many calls one client can make. | Per-IP and per-session rate limits (e.g. Upstash Ratelimit / Vercel KV), Vercel BotID or Turnstile, auth for heavy features, daily budget caps per user |
| Prompt injection by the listener ("ignore your instructions…", role-play, extracting the system prompt) | /api/ask, /api/define | Structured JSON output with a fixed action enum, so the model can only trigger 7 harmless client actions. sceneIndex is clamped client-side, and replies are rendered as text, not HTML. | Output moderation, keep untrusted input clearly delimited, refuse off-topic requests server-side, log and red-team |
| Adding tools to the tutor later (web search, code execution, agents) | /api/ask | Not applicable today: the tutor has no tools, so injected instructions can only change its words or pick one of the 7 actions | If tools are added: keep public traffic tool-less, or restrict to allowlisted domains with no network/sandbox. Cap tool calls, time and spend per request. Treat web results as untrusted (indirect prompt injection). |
| Indirect injection from book content (once users upload books, the text could contain "SYSTEM: …") | Build-time LLM, tutor context | N/A (the only book is trusted public-domain text) | Treat uploaded text as data: delimit it, strip control-like patterns, never give the build pipeline tools, validate every LLM output against its schema |
| Unsafe or inappropriate generations (images, Simple-English text, tutor answers) | Build + /api/ask | Provider safety filters; the content is a children's classic | Moderation on generated text and images before publishing, human review for public books, age-appropriate system prompts |
| Voice/TTS misuse (making the app speak arbitrary text) | GET /api/tts?text= | Voice allowlist, 1.2k-char cap |
unpdf) as a stretch. Detect chapters and paragraphs deterministically; use an LLM only to clean up odd cases.gpt-image-1-mini or Flux schnell, about 2–5 s) and show the text with a soft placeholder image until the art lands.sentenceRange, captionRange, alignment);npm install
echo 'OPENAI_API_KEY=sk-...' > .env.local
npm run dev # http://localhost:3000
Needs ffmpeg and ffprobe installed locally.
OPENAI_IMAGE_MODEL=gpt-image-2 npm run build:book # CHAPTERS=2 by default, + EXTRA_VOICES=coral,ash
CHAPTERS=1 MAX_SCENES=2 EXTRA_VOICES= npm run build:book # quick test
DRY=1 npm run build:book # just print the scene split
Every generated file is reused on re-runs. Delete a file in public/books/alice/ or .cache/alice/ to regenerate it.
OPENAI_API_KEY.| Variable | Default | Used by |
|---|---|---|
OPENAI_API_KEY | (required) | everything |
OPENAI_MODEL | gpt-4.1-mini | book build, /api/ask, /api/define |
OPENAI_TTS_MODEL | gpt-4o-mini-tts | build narration, /api/tts |
OPENAI_TUTOR_VOICE | coral | /api/tts fallback voice |
OPENAI_STT_MODEL | gpt-4o-mini-transcribe | /api/transcribe |
OPENAI_IMAGE_MODEL | gpt-image-1 | build (we used gpt-image-2) |
OPENAI_VOICE / EXTRA_VOICES | fable / coral,ash | build: default and extra narrator voices |
Only speak text the tutor produced: sign/HMAC the reply ID from /api/ask, or return audio from /api/ask itself |
| Secrets | OPENAI_API_KEY | Server-side only; .env* is git-ignored | Use project-scoped keys with only the needed model permissions, rotate them, and use separate keys per environment |
| Copyright | Uploaded books | Demo uses public-domain text | Restrict uploads to public-domain or user-owned content, keep each user's uploads private, add terms of use and a takedown process |