💡 Inspiration

English teachers run their whole working life out of five browser tabs and a prayer:

  • 📄 a pdf book / Mirro board for the lesson
  • 🎥 Google Meet / Zoom for the video call
  • Google Drive to host and share bunch of extra materials
  • 💬 Telegram / WhatsApp messanger for texting/homework
  • 📊 an Excel spreadsheet for who paid
  • 📓 a notebook for teacher's personal notes about each student

I watched this up close, and it looked less like teaching and more like air-traffic control.

I'm Oleksandr. I'm from Ukraine — because of the war I had to leave, and now I live in the Czech Republic. I'm a self-taught developer: YouTube videos, books, then a local company that was patient with me. I have a fulltime job, renting a cute studio apartment, and have a loving wife who supports me in anything I do. And that all above is already my biggest win.

I learned to code from strangers on the internet, and I learned English from teachers who deserved better tools than bunch of tabs and a spreadsheet. On the About page of teachueasy.com there's a line I mean completely:

❤️ This project is my huge passion, my "Pursuit of Happyness."

Building one calm place for teachers to work felt like the most useful thing I could point my evenings at.


🚧 What was built during Build Week

(Build Week — in my case, just the weekends. The day job pays the salary, the salary funds the project, and the weekends fund the hackathon.)

TeachUEasy already existed — the Build Week work is two features and the engineering around them, built with Codex on top of GPT-5.6:

1. 🗣️ The Dialogue block — lessons that talk

A new first-class block type in the Lexical lesson editor, not a bolt-on widget:

  • 🤖 One structured call. GPT-5.6 Sol writes the whole lesson and exactly one story-connected Dialogue — 12–24 turns, 2–4 characters with personas and per-line delivery directions (whispering, excited, laughing, nervous, deadpan) — as a single compact JSON.
  • 🧪 A deterministic quality gate, zero extra model calls. A Zod validation layer checks cast size, turn count, ≤2,000 TTS characters, ≥60% vocabulary coverage in the transcript, and ≥2 story anchors that visibly reappear in the dialogue — because paying a second model to doubt the first one is how you go broke politely. Invalid output gets re-asked, not shipped.
  • 🔊 A full audio cast. ElevenLabs eleven_v3 Text-to-Dialogue renders each line with word-level timestamps, so playback does karaoke-style word highlighting, with 0.5×–2× speed control and per-student local playback (no echo when a whole classroom hits play).
  • 🎨 Illustrated scenes. GPT Image 2 paints 0–2 scenes at 1024×1024, converted to WebP and anchored inline between specific turns — or the teacher uploads her own image instead.
  • 🙂 Avatars for free. DiceBear Avataaars rendered locally to data URIs — deterministic seeds, no runtime HTTP, zero cents per avatar.
  • 📖 Clickable vocabulary. The dialogue highlights the lesson's vocabulary items; a tap opens definition, translation, pronunciation — wired into the student's personal dictionary.
  • 🔐 Content-addressed everything. Every audio clip and image carries a SHA-256 source hash. Editing a turn marks its audio stale instead of silently re-billing; regeneration is an explicit button, rate-limited to 6/min, and unchanged assets are cache hits re-billed at $0.

2. 📎 Added attachments to AI lesson generation — Luna reads, Sol writes

Lessons stopped coming from thin air:

  • 📤 Teachers attach up to 5 files (images, PDFs, documents — 10 MB each, 20 MB total) and up to 10 links, each tagged with an intent: use as context, recreate these activities, create similar, follow instructions…
  • 🔎 GPT-5.6 Luna (low reasoning effort, prompt-cached) reads the files straight from R2 URLs, opens the links with web search, and produces an evidence brief capped at 60,000 characters. Its system prompt forbids it from writing the lesson — Luna is a researcher, not an author.
  • ✍️ The brief lands in Sol's sanitized input block, so the generated lesson works from the teacher's actual textbook page instead of inventing one.

3. ⚡ The unglamorous part: speed, and never paying twice

  • 💾 Phase cache. Generation persists three resumable phases — sourceAnalysisrawDraftcompletedRaw — on the generation log. If the pipeline retries, it never re-pays for a finished Luna brief or Sol draft. I learned this lesson from an invoice once. Never again.
  • 🚀 Lesson first, media async. The teacher gets a usable lesson immediately; the cover, story images, dialogue audio and scenes arrive via four new background functions — each with optimistic locks and stale-hash guards, so a slow retry can never overwrite an edit the teacher made in the meantime.
  • 🛟 Transcript-first reliability. If ElevenLabs or the image model has a bad day, the transcript still ships. Audio is a bonus; the lesson is the product.
  • 💶 Honest pricing. The cost estimator prices the whole run before the teacher commits — Luna's reading, Sol's writing, images at a flat $0.13, audio at one credit per character.

And the part I find quietly funny: after all of that, the database migration is two columns and one index — the entire Dialogue lives inside the lesson document itself, versioned and frozen with the lesson like everything else. Nine new test files stand guard over it. 🛡️


🧩 What existed before Build Week

b1148acf12f152d6f1a755c2f32b2a2cce5cd57c - latest commit before working on Build Week features

TeachUEasy is an all-in-one platform for English teachers. One login, one dashboard, zero tab-juggling:

  • ✍️ Interactive lessons — rich-text editor with exercises embedded right in the steps
  • 🖥️ Live classrooms — teach over Zoom or Google Meet with the lesson on screen
  • 📚 Homework — assign, review, give feedback, no paperwork
  • 💬 Real-time chat with students, in context
  • 📅 Scheduling — set availability, let students book, sync with Google Calendar
  • 💶 Payment tracking — no spreadsheets
  • 🧠 Personal vocabulary dictionary per student, with audio and 10 practice games — flashcards with spaced repetition, a crossword, a word constructor, and yes, a falling-words arcade 🕹️, because vocabulary review should occasionally feel like 1985
  • 🌍 Interface in English, Ukrainian, and Czech. Free to start, no credit card.

Then there's the AI layer:

  • ⚡ Type a topic, pick a level and duration → get a complete lesson (story, exercises, cover image) in a couple of minutes
  • 🤖 A per-lesson AI chat inside the editor that edits blocks the way a coding agent edits a codebase — preview → apply → undo

🛠️ How I built it

The timeline, honestly:

  • 📅 Feb 23, 2024 — first commit. Monorepo, MongoDB, complex deploys using Google Services, enormous dreams.
  • 💤 Then… silence for over a year. I'd love to give a dramatic reason. The honest one: day job, relationship complications, war... and a stack I secretly didn't believe in.
  • 🔄 Summer 2025 — came back, moved infrastructure to simpliest things: Next.js + PostgreSQL + Prisma. That was my the best decision.
  • 🧨 Aug 3, 2025 — made a commit literally titled START OVER. Everything you can click today was built after that commit.
  • 📺 April 2026 — became a YouTuber (more on that below).
  • 🚀 July 2026 — Build Week: AI lesson generation flow refactor and logic extending, the Dialogue feature ships.

The stack: Next.js + React + TypeScript · tRPC · Prisma on PostgreSQL · Meta's Lexical for the lesson editor · Tailwind + shadcn · Supabase broadcast for realtime · Stripe · Cloudflare R2 · Inngest and Vercel Workflow · Sentry + PostHog watching everything.

The scale today:

📊
Commits 431
Lines of code ~594,000 across ~3,000 files
Prisma schema 128 models · 71 enums
Pages 87
tRPC routers 50
Developers 1 🙋

The arithmetic only works because of AI coding agents.

Which brings me to the part where I became a YouTuber. 🎬 In April 2026, two years after starting the project, I created a channel — @The_Couch_Entrepreneur — and the reasoning was not mysterious: the project runs on leftover salary, so maybe a channel could earn money to feed the project. Build in public, livestream the real process, let monetization cover the AI bills instead of my dinner. The first stream, April 19th, was me wiring Stripe subscriptions into the platform live with Claude Code. I streamed the AI chat tuning, the classroom review, pricing decisions — all of it, unedited, bugs on camera.


🧗 Challenges I ran into

🚧 Build Week, honestly

The Dialogue feature was essentially one long Codex session: 47 prompts across the final three days (~46 hours wall-clock), 374 file edits, 34 web searches — and 18 context compactions, meaning the session literally ran out of memory for my complaints and had to summarize them. Here's what actually fought back:

🤖 The models

  • The very first generation died on a technicality: OpenAI structured outputs rejected my Zod schema'uri' is not a valid format — before any AI got a chance to be smart.
  • Prompt caching was silently doing nothing: Luna's prompt put the teacher's data before the static rules, so no prefix ever matched the cache. Static part first, dynamic part last — obvious in hindsight, invisible in the bill until you look.
  • Then the billing mysteries: Luna fired twice, Sol not at all. "I don't see Sol being run in logs" is a real quote from me to Codex. Untangling which retry re-paid for what is exactly why the three-phase generation cache exists.
  • And when the structure was finally correct, the content wasn't. My actual prompt: "dialogue content quality is really bad… write like a real human, a professional author." The schema guarantees shape; taste took several prompt rewrites.

🔊 The audio

  • ElevenLabs clips "failed quickly and remained queued for media retry" — my favorite kind of failure: fast and stuck.
  • "Generate media" proudly produced the illustration and forgot the audio existed.
  • Vocabulary words came back without pronunciation, which forced a re-architecture: dictionary enrichment first (definitions, translations, IPA), audio generation only after IPA exists, strictly one TTS job at a time in a serial queue.
  • A real design tradeoff: one audio clip per line, not per dialogue. Editing one sentence re-bills one sentence instead of the whole scene — but each line loses full-scene context for the voice actor. I chose the teacher's wallet.

🔊 Preparing for submission

  • I basically run out of time.
  • Editing tool got broken my PC got stuck. I barely edited and mounted it in time
  • Credits token usage get run out too :)

one teacher at a time, until this thing shines bright like a star. ⭐

Built With

  • cloudflare-r2
  • elevenlabs
  • google-oauth
  • inngest
  • lexical
  • next.js
  • node.js
  • openai
  • postgresql
  • posthog
  • prisma
  • react
  • redux-toolkit
  • sentry
  • shadcn/ui
  • stripe
  • supabase
  • tailwind-css
  • tanstack-query
  • trpc
  • typescript
  • vercel
  • vercel-ai-sdk
  • zod
Share this project:

Updates