Pick a Cute Story to Read! ๐โจ
Read the big easy words, listen to the voice ๐, answer the fun quiz ๐ฏ, and win your personalized Superstar Certificate! ๐๐
ุงุฎุชุงุฑู ูุตุฉ ุฌู ููุฉุ ุงุณุชู ุนู ูููุทู ุงูุฅูุฌููุฒูุ ุฃุฌูุจู ุนู ุงูุฃุณุฆูุฉ ุงูุณููุฉุ ูุงุญุตูู ุนูู ุดูุงุฏุฉ ุจุงุณู ู!
Story Title
โจ Tap the picture for magic sparkles! โจ
Fun Story Quiz! โข ุณุคุงู ุงููุตุฉ ุงูู ู ุชุน
ุฃุฌูุจู ุจุดูู ุตุญูุญ ูุชุญุตูู ุนูู ุงูุงุญุชูุงู ูุดูุงุฏุฉ ุงููุงุฑุฆุฉ ุงูู ุจุฏุนุฉ! ๐
Question goes here?
Mashallah! You Did It! ๐
You answered all the questions! What is your name, Superstar?
ู ุง ุดุงุก ุงููู! ุฅุฌุงุจุงุชู ูููุง ุตุญูุญุฉ! ุงูุชุจู ุงุณู ู ุงูุฌู ูู ููุทุจุน ุดูุงุฏุชูู ๐๐
Or tap your name quickly โข ุฃู ุงุถุบุทู ุนูู ุงุณู ู:
๐ Pick Your Certificate Sticker (ุงุฎุชุงุฑู ู ูุตู ุงูุดูุงุฏุฉ):
Reading Superstar Certificate
ุดูุงุฏุฉ ุชููู ูููุงุฑุฆุฉ ุงูุตุบูุฑุฉ ุงูู ุจุฏุนุฉ ๐
This special award is proudly presented to:
For reading the English story and answering all quiz questions with 100% accuracy!
ููุฑุงุกุชูุง ุงููุตุฉ ุงูุฅูุฌููุฒูุฉ ุจุจุฑุงุนุฉ ูุงูุฅุฌุงุจุฉ ุนู ุฌู ูุน ุงูุฃุณุฆูุฉ ุจูุฌุงุญ!
Date Earned โข ุงูุชุงุฑูุฎ
October 18, 2026
English Teacher โข ู ุนูู ุฉ ุงููุบุฉ ุงูุฅูุฌููุฒูุฉ
Teacher Story Studio & Manager
ููุญุฉ ุงูู ุนูู ุฉ: ุฅุถุงูุฉ ูุตุต ูุตูุฉ ุฃู ู ุตูุฑุฉ ุตูุญุฉ ุจุตูุญุฉุ ุญุฐู ุฃู ุงุณุชุนุงุฏุฉ ุงููุตุตุ ูุงูุฑุจุท ู ุน ูุงุนุฏุฉ ุจูุงูุงุช Cloudflare D1
๐ Student Reading Achievements & Certificates
ุณุฌู ุฅูุฌุงุฒุงุช ูุดูุงุฏุงุช ุงูุทุงูุจุงุช: ูู ุนุฑูุฉ ู ู ูุฑุฃุช ุงููุตุต ูุฃุฌุงุจุช ุนูู ุงูุฃุณุฆูุฉ ูู ูุงูุฃุชูู ๐
Total Stories Finished
ุฅุฌู ุงูู ุงููุตุต ุงูู ูุชู ูุฉ
Star Students
ุนุฏุฏ ุงูุทุงูุจุงุช ุงูู ุชู ูุฒุงุช
Super Certificates
ุดูุงุฏุงุช ุงูุชููู ุงูู ู ููุญุฉ
Loading student records...
Create a New Story for Your Students ๐ธ
Choose Just Text (fast & simple) or Image-by-Image (upload pictures or pick cute illustrations page by page).
๐ผ๏ธ Story Pages (Image + Sentence per Page)
For each page, upload an image from your device, paste an image link, OR choose a cute built-in illustration scene!
๐ Just Text Story Mode
Write or paste the story text below. Put each page/sentence on a new line, or keep it as one cozy paragraph!
๐ฏ End-of-Story Quiz Question (ุณุคุงู ููุงูุฉ ุงููุตุฉ)
Students answer this question after reading to trigger the confetti and earn their certificate!
๐๏ธ All Stories in the Library
You can delete any storyโincluding the 10 hardcoded starter storiesโor restore deleted starter stories anytime.
Host on Cloudflare Pages & D1 Database
This website is pre-wired with a hybrid Cloudflare D1 REST API + LocalStorage adapter. The 10 illustrated starter stories are hardcoded in the app AND can be deleted by the teacher (deletions are saved as tombstones in D1 `deleted_stories` and `localStorage`).
๐ Backend API Endpoint Configuration
Status: Auto-syncing with /api/stories when hosted on Cloudflare Pages, with instant browser storage fallback.
schema.sql)
-- Cloudflare D1 Schema for Little Blossoms Stories
CREATE TABLE IF NOT EXISTS custom_stories (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
title_ar TEXT,
category TEXT DEFAULT 'Teacher',
emoji TEXT DEFAULT '๐',
story_type TEXT DEFAULT 'illustrated',
pages_json TEXT NOT NULL,
quiz_json TEXT NOT NULL,
created_at INTEGER DEFAULT (strftime('%s', 'now'))
);
-- Stores IDs of hardcoded stories deleted by the teacher
CREATE TABLE IF NOT EXISTS deleted_builtin_stories (
story_id TEXT PRIMARY KEY,
deleted_at INTEGER DEFAULT (strftime('%s', 'now'))
);
functions/api/stories.js)
export async function onRequest(context) {
const { request, env } = context;
const url = new URL(request.url);
const cors = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
"Content-Type": "application/json"
};
if (request.method === "OPTIONS") return new Response(null, { headers: cors });
// GET all custom stories + deleted hardcoded story IDs
if (request.method === "GET") {
const { results: customStories } = await env.DB.prepare(
"SELECT * FROM custom_stories ORDER BY created_at DESC"
).all();
const { results: deletedRows } = await env.DB.prepare(
"SELECT story_id FROM deleted_builtin_stories"
).all();
return new Response(JSON.stringify({
customStories: customStories.map(r => ({
id: r.id, title: r.title, titleAr: r.title_ar,
category: r.category, emoji: r.emoji, storyType: r.story_type,
pages: JSON.parse(r.pages_json), quiz: JSON.parse(r.quiz_json),
isBuiltin: false
})),
deletedBuiltinIds: deletedRows.map(r => r.story_id)
}), { headers: cors });
}
// POST new story or restore built-in stories
if (request.method === "POST") {
const body = await request.json();
if (body.action === "restore_builtins") {
await env.DB.prepare("DELETE FROM deleted_builtin_stories").run();
return new Response(JSON.stringify({ ok: true }), { headers: cors });
}
const s = body.story;
await env.DB.prepare(
`INSERT OR REPLACE INTO custom_stories
(id, title, title_ar, category, emoji, story_type, pages_json, quiz_json)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
).bind(
s.id, s.title, s.titleAr || "", s.category || "Teacher",
s.emoji || "๐", s.storyType || "illustrated",
JSON.stringify(s.pages), JSON.stringify(s.quiz)
).run();
return new Response(JSON.stringify({ ok: true, id: s.id }), { headers: cors });
}
// DELETE a hardcoded story (tombstone) or teacher story
if (request.method === "DELETE") {
const id = url.searchParams.get("id");
const isBuiltin = url.searchParams.get("builtin") === "1";
if (isBuiltin) {
await env.DB.prepare(
"INSERT OR IGNORE INTO deleted_builtin_stories (story_id) VALUES (?)"
).bind(id).run();
} else {
await env.DB.prepare("DELETE FROM custom_stories WHERE id = ?").bind(id).run();
}
return new Response(JSON.stringify({ ok: true, deletedId: id }), { headers: cors });
}
return new Response(JSON.stringify({ error: "Method not allowed" }), { status: 405, headers: cors });
}