Build

Interactive canvas

Kid-facing HTML in a sandboxed iframe. Avatar reactions via the Sprout SDK. Single completion call.

What you'll build

Your kid plays an interactive moment on the iPad: a mini-game, a math drill, a drawing, a quiz, a narrated story. Sprout's avatar reacts in real time to what they're doing, celebrating right answers, encouraging on misses, noticing when they're stuck. Completion gets scored, timed, or marked done.

The artifact is a one-off or recurring task in the kid app, with custom interactive HTML rendered inside it.

Pieces you'll combine

Build it

Five movements:

1. Author the canvas (dry-run). Two-step commit so the parent sees a preview before anything ships.

Shell
canvas.create({
name: "<activity name>",
emoji: "🌟",
html: `<section id="content"><!-- your kid-facing HTML --></section>
<script>
sprout.signal("celebration");
sprout.signal("attempt-failed");
sprout.signal("user-stuck");
sprout.score({ value: finalScore, max: maxScore });
</script>`,
completionSchema: { kind: "score", maxScore: 5 },
dimensions: { age: "8-9" },
dryRun: true
})
# Returns { previewHtml, analyzerIssues, specHash }

2. Show the preview, get the OK, commit.

Shell
canvas.create({ ...same input..., dryRun: false, specHash: "<echoed>" })
# Returns { canvasId }

3. Wrap in a skill so the canvas has a governed home in your library.

Shell
skill.write({
name: "<activity name> canvas skill",
description: "Deliver the linked canvas to a kid as a one-off task.",
category: "home_agent",
prompt: "Create a task for {{input.child_id}} with the linked canvas, due {{input.date}}, with a {{input.gems}}-gem reward.",
handsReferenced: ["task_create"],
inputVariables: [{ name: "child_id" }, { name: "date" }, { name: "gems" }],
canvasIds: ["<canvasId>"]
})
# Returns { skillId }

4. Invoke the skill to deliver the task.

Shell
skill.invoke({
skillId: "<skillId>",
input: { child_id: "<kidId>", date: "2026-05-24", gems: 5 }
})
# Your agent renders the prompt, calls task.create with runMode "canvas".

5. Hook up avatar reactions inside the canvas HTML using the SDK. The signals fire mid-activity; the completion call fires once at the end.

JavaScript (inside the canvas)
// Mid-activity signals
sprout.signal("celebration");
sprout.signal("attempt-failed");
sprout.signal("user-stuck");
// Mandatory single completion call
sprout.score({ value: 4, max: 5 });
// Or: sprout.complete({ summary: "..." })
// Or: sprout.timed({ durationSeconds: 287 })

6. Wire memory so the kid resumes. Write durable run state into sprout.state and it auto-persists as the kid works , no save button. On reopen the host seeds it with the saved snapshot before your code runs, so default-fill what's missing and branch on sprout.resumed. Never assign sprout.state = {…} wholesale , it wipes a resumed run.

JavaScript (inside the canvas)
const S = sprout.state;   // saved snapshot on resume, {} on a fresh start
S.step ??= 0;             // default-fill ONLY what's missing
S.answers ??= {};
if (sprout.resumed) goToStep(S.step);   // returning mid-run , rebuild UI
else showIntro();                        // brand-new run , intro / tutorial
S.answers.q1 = "blue";   // mutate freely; every change auto-persists

Full memory pattern: Canvases that remember.

Full SDK contract: resources/read sprout://canvas/sdk.

When to use it

Tools touched

Recommended skills

Drop these into your library to compose with this pattern.

Screen readerin prodQuiz templatetemplate

Roadmap

Seen in walkthroughs

Solar system learning loop Chore + photo proof

Was this page helpful?