Build a ~20-line JS Program (with notes):
// Player: build a small JS program (fetch + timing) in readable steps
const steps = [
{ action:'clear', note:'Start from a blank document.', waitAfterMs:150 },
{ action:'set',
args:{ code:''use strict';\n// Demo: fetch a small JSON and time the request\n', typed:true },
note:'Insert the header and a short description.', waitAfterMs:200 },
{ action:'append',
args:{ code:
class Timer { constructor(){ this.t0 = 0; this.t = 0; } start(){ this.t0 = performance.now(); } stop(){ this.t += performance.now() - this.t0; } reset(){ this.t = 0; } ms(){ return this.t.toFixed(1); } } , typed:true },
note:'Add a tiny Timer utility to measure elapsed time.', waitAfterMs:200 },
{ action:'append',
args:{ code:
async function fetchJSON(url){ const res = await fetch(url); if(!res.ok) throw new Error(res.status + ' ' + res.statusText); return res.json(); } , typed:true },
note:'Helper that fetches JSON and throws on HTTP errors.', waitAfterMs:200 },
{ action:'append',
args:{ code:
async function main(){ const t = new Timer(); t.start(); const data = await fetchJSON('https://jsonplaceholder.typicode.com/todos?_limit=5'); t.stop(); const done = data.filter(x => x.completed).length; console.log('Loaded ' + data.length + ' todos; ' + done + ' completed in ' + t.ms() + 'ms'); } , typed:true },
note:'main(): time the request, fetch 5 items, compute a stat, log a summary.', waitAfterMs:250 },
{ action:'append',
args:{ code:
main().catch(err => { console.error('Request failed:', err.message); }); , typed:true },
note:'Call main() and handle errors.', waitAfterMs:250 },
{ action:'searchThenPopover',
args:{
query:'jsonplaceholder.typicode.com',
options:{ inline:true, inlineHL:true, viewOffsetPx:64 },
popover:{ title:'Data source', content:'Using a public placeholder API.', direction:'bottom' }
},
note:'Explain the URL being used.', waitAfterMs:400 },
{ action:'toast', args:{ text:'Script ready — try it!', type:'success' },
note:'Friendly confirmation toast.' },
{ pause:true, note:'Pause so readers can review before running.' }
];
api.loadSteps(steps, { autoplay:false, playerShowNotes:true });
// api.playerPlay();
Good to know
Steps are declarative and composable. Keep scripts small and labeled; mix delays for pacing, pauses for interaction, and questions for branching. You can show/hide player UI via options and drive everything programmatically with playerPlay, playerPause, playerNext, playerPrev, and playerGoto.