← All posts
PROJECT
Language Learning · AI · Web Development
Teaching AI to write me a new story every day
How I built Daily Story—fresh reading practice in eight languages, calibrated to your level—and what it taught me about prompt engineering.
stack Node.js · Express · Claude AI
status live
Building Language Immersion Without a Plane Ticket
Language immersion doesn't require a plane ticket. Daily Story delivers fresh, AI-generated stories in eight languages across four skill levels—beginner to advanced. Each story comes with comprehension quizzes to reinforce what you've learned and track your progress.
Whether you're brushing up on Spanish before a trip or diving deep into Japanese grammar, Daily Story gives you something new to read every single day, perfectly calibrated to challenge you without overwhelming you.
The Problem with Finding Content at Your Level
One of the best ways to learn a language is to immerse yourself in it through reading. The problem? Finding content that matches your exact skill level is difficult. Too easy and you're not learning; too hard and you're frustrated.
I found myself constantly asking ChatGPT to generate short stories at my skill level for language practice. It worked, but it was tedious to do manually every day. I realized this could be automated into a daily habit-building tool that others could benefit from too.
Teaching AI to Curate, Not Just Generate
Building Daily Story taught me that working with AI is less about the code and more about the conversation. The technical infrastructure was straightforward, but getting consistent, quality output required a completely different skill set.
The real challenge wasn't the architecture—it was prompt engineering.
After a few days of testing, I noticed the AI kept generating eerily similar stories. Without memory of what it had created before, the model naturally gravitated toward the same narrative patterns. The solution? A curated library of about 100 themes per skill level that the app randomly selects from before each generation.
const themes = {
a1: [
"introducing yourself and saying hello",
"saying where you are from",
"ordering coffee or tea"
],
a2: [
"planning a weekend trip with friends",
"discussing your favorite restaurant",
"discussing why you like your job"
],
b1: [
"talking about changing careers or jobs",
"explaining how you balance work and personal life",
"discussing a time you had a disagreement with someone"
],
b2: [
"debating the ethics of human enhancement technologies",
"analyzing the role of traditional media versus independent journalism",
"evaluating the long-term consequences of the gig economy"
]
};
This wasn't just about variety for variety's sake. A theme like "navigating cultural misunderstandings" works beautifully for intermediate learners but falls flat for beginners who need simpler vocabulary. Each proficiency level needed its own theme collection, carefully chosen to match both linguistic complexity and conceptual accessibility.
Another major shift came from rethinking the story format itself. Initially, all stories were fables—traditional tales with morals and lessons. But fables proved too challenging for absolute beginners and too simplistic for upper-intermediate readers. More importantly, fables rely heavily on past tense narration, which doesn't reflect the language learners actually use in real conversations. I pivoted to a conversation format between two or more people. This made stories immediately more relevant across all skill levels, focusing on the present-tense dialogue and natural speech patterns that learners encounter in daily life.
Architecture Designed for Cost Efficiency
The architecture is intentionally straightforward. Built with Node.js and Express, the app runs on a simple cron schedule that handles AI content generation efficiently and cost-effectively.
The app runs on two cron schedules. At midnight, it submits a batch request to the Claude API for 32 different stories—8 languages at 4 skill levels each. Batch requests process at half price but can take up to 24 hours. At 6 PM, the app retrieves any completed stories and caches them, ensuring they're ready with no delay when midnight rolls around. If stories aren't ready by 6 PM, they're picked up during the next midnight cycle when submitting the following day's request.
By retrieving stories at 6 PM, they're ready the moment users need them at midnight—eliminating any lag while still taking advantage of half-price batch processing.
Running on Google Cloud Run means I only pay for active compute time. No database overhead, no complex state management—just reliable, daily language practice delivered through a clean API.
Tech Stack
Node.js
Express
Claude AI
Lessons in Prompt Engineering
This project transformed how I think about working with AI. I expected the hard part to be building the infrastructure—cron jobs, API integration, deployment. But the real challenge was prompting the large language model to consistently generate stories that felt authentic and educational across eight different languages.
I spent days iterating on prompts, discovering that small changes in phrasing could mean the difference between a compelling story and something that read like a textbook exercise. Quality matters more than quantity—each story needs to be genuinely engaging and leave learners feeling measurably better at their target language.
The trickiest part? Getting consistent JSON responses across all languages. The LLM would sometimes fail to properly escape characters or add characters that would break the JSON parsing—especially problematic when working with eight different languages, each with unique character sets and punctuation. After countless malformed responses, I discovered structured output tools specifically designed for this purpose. Using these tools ensures the LLM generates properly formatted JSON every single time, regardless of language. What started as my biggest pain point became a solved problem once I learned the right approach.