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
- Canvas: the kid-facing HTML rendered in a sandboxed iframe.
- Skill: wraps the canvas for governance and reuse.
- Task with
runMode: "canvas": delivers the canvas as a kid-visible activity. - The
sprout.*SDK: the only host bridge from inside the iframe. Drives avatar reactions and the completion call.
Build it
Five movements:
1. Author the canvas (dry-run). Two-step commit so the parent sees a preview before anything ships.
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.
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.
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.
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.
// 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 })Full SDK contract: resources/read sprout://canvas/sdk.
When to use it
- The kid is doing something interactive: game, drill, drawing, quiz.
- You want visual structure beyond a chat message.
- Completion depends on a structured artifact (score, timing, captured photo).
- Anti-pattern: the kid is sharing or reflecting. Use Conversational task instead.
Tools touched
canvas.create,canvas.update: author and edit the canvas HTML.skill.write: wrap the canvas in a governed skill.task.createwithrunMode: "canvas": deliver to the kid.
Recommended skills
Drop these into your library to compose with this pattern.
Roadmap
- Soon Storage-backed canvases. Persistent kid-scoped state across sessions. Save progress, build on what came before.
- Soon Asset upload.
sprout.uploadAsset()lets the canvas capture a photo or short video as completion evidence. See the Photo and video proof pattern for the full shape.
Seen in walkthroughs
Solar system learning loop Chore + photo proof
Related patterns
- Conversational task: when the kid should talk, not click.
- Autonomous loop: pair to schedule the canvas on a cadence.