Set the scene.
Collect the finished film.
Two images, your words, your rules. Give an agent a complete level, let bounce record it, then take the video wherever you want.
- 01Supply a levelTwo images, a phrase, settings and powers.
- 02Record the sceneWait for the phrase to leave, or set a duration.
- 03Save, then repeatDownload the file. Submit the next job when ready.
One runner. Two ways in.
For an agent that needs a file on disk, run bounce locally and use HTTP. For an agent controlling this browser, call window.bounceAgent in the runner tab. The published site supports the browser API; the REST service is provided by npm start.
Start the server.
npm start
# Open http://127.0.0.1:4173/?agent=1
# Keep that runner tab visible while recording.Keep one runner tab open. The server stores job status and receives the finished video. A separate Node process can submit jobs and save the bytes.
Call the runner directly.
// In the /?agent=1 runner tab:
window.bounceAgent.capabilities()
window.bounceAgent.status()Opening the runner waits for instructions. It never starts a job by itself. Do not switch to this guide while a recording is running.
Keep the runner visible and the device awake. Canvas recording needs an active browser tab. Hiding the tab can stop the recording early. Your agent can call the local HTTP service from the background while the runner stays in front.
Get a video onto disk.
Save the job JSON below as job.json. The included Node.js 20+ helper accepts your two local images, submits the job, waits for completion, saves the video, and releases the server’s copy only after the file is written.
node scripts/agent-record.mjs --job job.json \
--image ./images/first.png --image ./images/second.jpg \
--out ./rendersOn Windows PowerShell, put this command on one line. Alternatively, include two image URLs or data URLs in job.json and omit both --image arguments.
A complete, portable job.
This example includes every configurable field. Replace the two example image URLs with your own direct PNG, JPEG or WebP URLs, or let the helper embed your local files. The first image is the level; the second is the image underneath it when nextImage is enabled.
Loading the current job contract…Images
Exactly two {url, name?} objects. Use self-contained data URLs for the most reliable automation. Remote images must permit CORS for this app’s origin. file:// paths are not accepted. Image load failures fail the job rather than substituting a different picture.
Phrase & settings
A nonempty, renderable phrase is required. Validation checks the phrase, available dimensions and readable letter forms before recording. All job settings are temporary: they do not change your saved image library or playground preferences.
Validate before you record.
const response = await fetch('http://127.0.0.1:4173/api/v1/validate', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify(spec)
});
const validation = await response.json();
if (!response.ok) throw new Error(JSON.stringify(validation.error));
// {valid: true, spec: normalizedSpec}Every setting, in one place.
Omitted optional fields use the defaults shown below. Unknown fields and out-of-range values are rejected, so mistakes are caught before a video starts. The reference is generated from the same contract the runner and server validate.
Portrait uses a 765 × 1360 simulation; landscape uses 1360 × 765. Recording resolution sets the exported frame size, while ball and block sizes refer to this simulation. Phrase fitting may use smaller letter cells to preserve the requested image block size.
You choose the available powers.
Set powerups to an array of exact IDs. Agent jobs use this explicit selection instead of the normal progressive unlock plan. Include only the powers you want; [] disables all powerup spawns. The timed interval, ball growth limit and low-FPS multiplication guard still apply.
Speed and other personal abilities apply to the ball that collected them. Piercing still bounces off letter walls. Growth powers can be replaced when the configured population limit is reached.
Local REST API.
Base URL: http://127.0.0.1:4173/api/v1. Start npm start and keep one runner tab connected. Check GET /api/v1 for the current capabilities and worker.online before submitting.
| Method & path | Result |
|---|---|
GET /api/v1 | Versioned capabilities, defaults, schema, powers and runner status. |
POST /api/v1/validate | Validate a job body. Returns {valid:true, spec}, or an error. |
POST /api/v1/jobs | Submit the job body. Returns HTTP 202 with its id and statusUrl. One active job at a time. |
GET /api/v1/jobs/:id | Poll the job. Terminal states are complete, error, cancelled. |
GET /api/v1/jobs/:id/video | Download the completed video bytes. Use result.mimeType and result.filename. |
POST /api/v1/jobs/:id/cancel | Cancel a queued or active job. Cancellation discards its recording. |
DELETE /api/v1/jobs/:id | Release a terminal job and its video. Call only after your file is saved. |
For safe submit retries, send an Idempotency-Key header unique to that intended job. Keep the returned job ID. Poll that ID instead of blindly submitting again if a request times out.
Loading workflow…The local service binds to loopback. It is for a trusted agent on this computer, and is not an authenticated public render service. Cross-origin browser requests may be refused; run HTTP requests from Node or another local process. Hosted static pages do not provide these REST endpoints.
A direct API in the runner.
These methods are available as window.bounceAgent on the playground. For automation, use /?agent=1 so it begins idle. run(spec) returns a job status immediately while preparation and recording proceed asynchronously.
| Method | Use |
|---|---|
version | Contract version, currently 1. |
capabilities() | Read defaults, schema, available powers and limits. |
validate(spec) | Validate a level without starting it. |
run(spec) | Start one job. Refuses while another is active. |
status() | Read state, progress, errors and result metadata. |
pause() / resume() | Pause or resume the running scene. |
stop() | Finish early and retain the video recorded so far. |
cancel() | Cancel the job and discard the video. |
getVideo() | Get the completed Blob, object URL and file metadata in this browser. |
download() | Request a browser download. Browser download permissions still apply. |
release() | Release the retained recording after saving it. |
const api = window.bounceAgent;
const validation = api.validate(spec);
if (!validation.valid) throw new Error(JSON.stringify(validation.error));
const started = api.run(spec); // prepares in the background
// Poll api.status() until state === 'complete'.
const video = api.getVideo(); // Blob + metadata + object URL
api.download(); // browser-controlled download
// After confirming your file was saved:
await api.release();
// Await your next explicit api.run(nextSpec).WebMCP, where supported.
Browsers exposing document.modelContext or navigator.modelContext can offer these tools to an agent: bounce_get_capabilities, bounce_validate_job, bounce_start_job, bounce_job_status, bounce_pause_job, bounce_resume_job, bounce_stop_job, bounce_cancel_job, bounce_download_video and bounce_release_video. Support is optional; use the direct browser API or local REST when unavailable.
A job ends. The runner waits.
queued→preparing→recording→completeWith recording.duration: "level", recording ends after the image clears and the phrase completes its hold and exit animation. A fixed number records for that many seconds; 0 records until stopped or the safety timeout is reached. No job automatically launches another job or advances into another recorded level.
Finished product
A clean video with no dashboard controls or information overlays. The requested phrase animation remains part of the canvas. Read the MIME type and filename instead of assuming MP4: support depends on the browser. Videos have no audio. The result reports completionReason: level, duration or manual. progress.completedLevel is true only after the level and phrase exit finish.
Timeouts & partial clips
The configurable recording.maxSeconds is a safety deadline. Reaching it fails an unfinished job, with no finished video. Hiding the runner cancels its job. A manual stop() can retain a partial clip: check result.completionReason and progress.completedLevel before treating it as a completed level.
Recover without losing a result.
- Validation errors identify the field to fix. Run validation before starting a long recording.
- If no worker is online, open the local runner. Keep only one local runner tab connected.
- HTTP 409 means a conflicting active job or lifecycle action. Poll the existing job rather than creating duplicates.
- A failed or cancelled job has no downloadable video. Read
error, correct the problem and submit a new job. - Downloads are retained in memory. Save before closing the runner, restarting the server or releasing a result. The server keeps at most three completed recordings within its video memory budget.
- Cancel a stuck job, wait for a terminal state, then release it. To stop early and keep a clip, use the browser
stop()method.
Read error responses
{
"error": {
"code": "INVALID_JOB",
"message": "A description of the problem",
"path": "config.blockSize"
}
}Error fields describe the rejected input or lifecycle conflict. Image preparation, recorder support, browser visibility and worker disconnection can also fail or interrupt a job. Inspect status before treating the file as ready to publish.
The complete instruction handoff.
Copy this into your agent’s workflow. Download the reference JSON alongside it so the agent can discover the exact supported values.
Loading agent instructions…