Canvas bots
Interactive HTML the kid plays. Self-contained, CSP-locked, SDK-injected. The bread-and-butter primitive.
Interactive HTML the kid plays. Self-contained, CSP-locked, SDK-injected. The bread-and-butter primitive, fully shipped, fully audited, fully forkable.
Anatomy of a canvas bot
Up to 1MB HTML. Auto-injected SDK + design-system CSS. CSP-locked: no fetch, no external scripts, no localStorage. Talks to the host only through sprout.* + SproutBridge.
sprout.whoami()- greet by name, branch by kid age- Use design-system classes:
btn-primary,card,stack,input sprout.signal(...)on meaningful moments: celebration, attempt-successful, user-stucksprout.complete()+ the legacySproutBridge.postMessageon done (fire both, analyzer wart)
Four bot patterns
Evening reflection
Three questions, mood scored. Simple form canvas: mood slider, what-went-well text field, what's-tomorrow checkbox list. Declare a completionSchema with {mood: int, wins: string, plan: string[]} so the parent app surfaces structured data later. sprout.complete with the result.
Math walkthrough
Step-by-step, hints on stuck. Render one problem at a time. sprout.signal('attempt-successful') on correct, attempt-failed on wrong, user-stuck after three wrong in a row (parent gets a notification). sprout.score at the end. Branch difficulty by whoami().childAge (under 7, easier track).
Spelling bee
Paced rounds, signal on streak. The Web Audio API works inside the canvas: play the word, kid types the answer, you grade, signal. After 5 correct in a row, fire sprout.signal('celebration') (the avatar reacts). sprout.timed on completion so the parent sees how long.
Choose-your-own homework
Branching plan. Show 3 cards ("read first?", "math first?", "snack first?"), kid picks, canvas adapts. Each branch logs a different completionSchema path so the parent learns the kid's pattern over time. This is a canvas as a tiny app, not a worksheet.
What ships today
All of the above. canvas_create + skill_write + skill_activate + task_create is the canonical four-call sequence, and every canvas-bearing path through Sprout uses it. The SDK (sprout.whoami, sprout.signal, sprout.complete, sprout.score, sprout.timed) is auto-injected at commit time. Design-system tokens come with it.
Power-ups
Signals: six types: celebration, milestone-reached, attempt-successful, attempt-failed, user-stuck, hint-requested. The host (avatar, telemetry) reacts. Pick the most-specific one.
Completion shapes: complete() for binary done, score(score, total) for graded, timed(seconds) for held-duration.
Preview URL: every canvas_create returns a previewUrl on app.sproutgoodhabits.com/preview/canvas?previewToken=.... Play it in any browser exactly as the kid sees it.
Hidden wart
window.SproutBridge.postMessage(JSON.stringify({type: "scored", score, total})) on completion even though the SDK doc says use sprout.complete(). Fire both. The SDK has a double-fire guard. Canvas guts covers this in detail.
Try it
<!doctype html>
<html lang="en">
<body>
<h1>Hey, <span id="name">you</span>.</h1>
<p>How was today? (1 = rough, 5 = great)</p>
<div class="stack">
<button class="btn btn-primary btn-lg" onclick="pick(1)">1</button>
<button class="btn btn-primary btn-lg" onclick="pick(3)">3</button>
<button class="btn btn-primary btn-lg" onclick="pick(5)">5</button>
</div>
<script>
async function init() {
const me = await sprout.whoami();
document.getElementById('name').textContent = me.childName;
}
function pick(score) {
sprout.signal(score >= 4 ? 'celebration' : 'milestone-reached');
sprout.complete({mood: score});
// analyzer requires the legacy bridge too
window.SproutBridge.postMessage(JSON.stringify({
type: 'scored', score: 1, total: 1
}));
}
init();
</script>
</body>
</html>Then dry-run, wrap in a skill, activate, deliver. See the Build it section.