PROMPT Chrome Visual Testing
Documentation for Developers
Prompt: Visual Testing Vitto Avatar via Chrome Injection
Copy-paste this entire prompt at the start of a new Claude session when you need the agent to visually test, iterate on, or screenshot the Vitto Canvas 2D avatar.
Context for Agent
You are working on Vitto, a Canvas 2D animated black cat avatar. The avatar engine is a pure TypeScript library at libs/libs-anim/vitto-avatar/src/lib/engine/ inside the gspace1 NX monorepo. It renders a cat with 14 mood states (neutral, focused, cheerful, empathetic, curious, analytical, celebratory, apologetic, inspired, urgent, playful, confused, proud, sleepy), each controlling 30+ animatable properties (eyes, mouth, brows, body, particles, etc.) with smooth cubic-eased transitions.
Critical Constraint: You CANNOT Serve Files to Chrome
Your VM has no network bridge to the user's Chrome browser. localhost, 127.0.0.1, and any port you bind are unreachable from Chrome. Do not attempt to start HTTP servers — they will appear to work in your terminal but Chrome will show "ERR_CONNECTION_REFUSED."
The Workaround: Chrome JavaScript Injection into example.com
You have access to the "Claude in Chrome" extension which can execute JavaScript in browser tabs. The workflow is:
- Inject HTML/CSS into an
example.comtab viadocument.write() - Inject the engine as base64-encoded chunks
- Inject the UI harness script
- Take screenshots to visually verify rendering
Detailed Steps
Step 0: Build the Engine Bundle
If the bundle doesn't exist yet or if you've changed engine source files, build it:
cd /path/to/gspace1
npx esbuild libs/libs-anim/vitto-avatar/src/lib/engine/index.ts \
--bundle --format=iife --global-name=VittoEngine \
--outfile=dist/libs/libs-anim/vitto-avatar/engine/vitto-engine.iife.js \
--minify --sourcemap
The output should be ~35KB at dist/libs/libs-anim/vitto-avatar/engine/vitto-engine.iife.js.
Step 1: Get Chrome Tab Context
Use tabs_context_mcp with createIfEmpty: true
Look for an existing tab on example.com or create a new one. Note the tabId.
Step 2: Navigate to example.com (if needed)
If the tab isn't already on example.com, navigate there first.
Step 3: Inject the HTML Structure
The dev harness HTML lives at:
"vitto animation/vitto-dev-harness.html"
Read it, extract everything between <style> and </style> (the CSS), and everything between <body> and the first <script> (the HTML structure). Then inject via the Chrome JS tool:
document.write("<html><head><style>...CSS HERE...</style></head><body>...HTML HERE...</body></html>");
Escaping rules:
- Use
document.write("...")with double-quote outer wrapping - Escape any
"inside the content as\" - CSS
font-family: 'Segoe UI'— single quotes inside double-quote string are fine - Minify CSS to reduce size (remove newlines, collapse whitespace)
- Keep total JS call under ~8KB
The HTML structure includes:
<canvas id="vittoCanvas" width="840" height="840">— the render target#fpsBadge— FPS counter display#moodBadge— current mood name#moodGrid— container for mood buttons#autoCycleBtn+#autoCycleLabel— auto-cycle toggle#toggles— behavior toggle container#sliders— property slider container#transBar— transition progress bar#inspector— state inspector display
Step 4: Prepare Base64 Chunks of the Engine
python3 -c "
import base64
with open('dist/libs/libs-anim/vitto-avatar/engine/vitto-engine.iife.js', 'rb') as f:
encoded = base64.b64encode(f.read()).decode()
chunk_size = 8000
for i in range(0, len(encoded), chunk_size):
chunk = encoded[i:i+chunk_size]
with open(f'/tmp/b64chunk_{i // chunk_size}.txt', 'w') as out:
out.write(chunk)
print(f'Chunk {i // chunk_size}: {len(chunk)} chars')
print(f'Total base64 length: {len(encoded)}')
"
This typically produces 5-6 chunks of ~8000 chars each.
Step 5: Inject Base64 Chunks into Chrome
For each chunk file, read it and inject:
// First chunk — use assignment (=)
window._b64 = "...contents of b64chunk_0.txt...";
"chunk0: " + window._b64.length
// All subsequent chunks — use append (+=)
window._b64 += "...contents of b64chunk_1.txt...";
"chunk1: " + window._b64.length
// ... repeat for all chunks
Important: Each chunk injection must be a separate Chrome JS tool call. Keep each call under ~4000 chars of actual text content to stay within tool limits. If a chunk is too large, split it in half.
Verify final length matches the total from Step 4.
Step 6: Decode and Evaluate the Engine
try {
window.VittoEngine = (new Function(
atob(window._b64) + "; return VittoEngine;"
))();
"VittoEngine keys: " + Object.keys(window.VittoEngine).join(", ");
} catch(e) {
"ERROR: " + e.message;
}
Expected output should include: VittoRenderer, MOOD_DEFINITIONS, MOOD_NUMERIC_KEYS, DEFAULT_RENDERER_CONFIG, TAU, PI, lerp, clamp, easeInOutCubic, easeOutBack
Why new Function() instead of eval()? — eval() doesn't expose var declarations to the global scope inside Chrome extension's JS execution context. new Function() lets us explicitly return VittoEngine.
Step 7: Inject the UI Harness Script
Read the <script> section from vitto-dev-harness.html (lines 190-396). Split into 2-3 parts (~3000 chars each) and inject sequentially via Chrome JS tool.
Critical: All shared references must be stored on window.* because each Chrome JS tool call runs in a separate evaluation scope:
window._renderer— the VittoRenderer instancewindow._MOOD_KEYS— array of mood key stringswindow._sliderEls— map of slider DOM elementswindow._canvas— the canvas element
Part 1 should create: renderer, mood buttons, mouse tracking, FPS counter Part 2 should create: auto-cycle, behavior toggles, property sliders Part 3 should create: state inspector, syncSliders function, setInterval update loop
Step 8: Screenshot and Verify
Take a screenshot to verify the harness is working:
computer action: screenshot
You should see:
- Left panel: canvas with black cat avatar on colored background
- Right panel: mood grid (14 buttons), behavior toggles, property sliders, state inspector
- FPS badge in top-left corner (will show ~1fps due to background tab throttling — this is normal)
Changing Moods for Testing
Via JS tool:
window._renderer.setMood('cheerful');
document.querySelectorAll('.mood-btn').forEach(b => b.classList.toggle('active', b.dataset.mood === 'cheerful'));
document.getElementById('moodBadge').textContent = 'cheerful';
Force-completing transitions (bypasses the 1fps throttle):
var target = VittoEngine.MOOD_DEFINITIONS['urgent'];
var s = window._renderer.getState();
s.mood = 'urgent';
s.tT = 1.0;
for (var k of VittoEngine.MOOD_NUMERIC_KEYS) {
if (target[k] !== undefined) s[k] = target[k];
}
s.bg = [...target.bg];
Wait 2 seconds, then screenshot. The canvas will re-render on the next RAF tick.
Zooming into the Canvas
Use the zoom action to get a close-up of Vitto:
computer action: zoom, region: [5, 260, 280, 530]
(Adjust coordinates based on actual canvas position in the viewport.)
File Locations Reference
| File | Path | Purpose |
|---|---|---|
| Engine source | libs/libs-anim/vitto-avatar/src/lib/engine/ |
TypeScript source (single source of truth) |
| Engine entry | libs/libs-anim/vitto-avatar/src/lib/engine/index.ts |
Bundle entry point |
| Built bundle | dist/libs/libs-anim/vitto-avatar/engine/vitto-engine.iife.js |
~35KB IIFE bundle |
| Dev harness | "vitto animation/vitto-dev-harness.html" |
UI shell (zero engine code) |
| NX project | libs/libs-anim/vitto-avatar/project.json |
Has bundle-engine target |
| Moods | libs/libs-anim/vitto-avatar/src/lib/engine/moods.ts |
All 14 mood definitions |
| Renderer | libs/libs-anim/vitto-avatar/src/lib/engine/renderer.ts |
Core render loop + transitions |
| Drawing | libs/libs-anim/vitto-avatar/src/lib/engine/drawing/ |
background.ts, body.ts, face.ts, head.ts, effects.ts, shapes.ts |
| Behaviors | libs/libs-anim/vitto-avatar/src/lib/engine/behaviors/ |
breathing.ts, saccades.ts |
| Types | libs/libs-anim/vitto-avatar/src/lib/engine/types.ts |
MoodProperties, AnimState, DrawContext, RendererConfig |
| Math utils | libs/libs-anim/vitto-avatar/src/lib/engine/math.ts |
lerp, clamp, easeInOutCubic, easeOutBack |
| Angular component | libs/libs-anim/vitto-avatar/src/lib/components/vitto-avatar/ |
.ts, .html, .scss |
| Mood service | libs/libs-anim/vitto-avatar/src/lib/services/vitto-mood.service.ts |
Signal-based mood management |
Engine Architecture (for context)
VittoRenderer (renderer.ts)
├── constructor(canvas) → starts RAF loop
├── setMood(key) → begins interpolated transition
├── render(timestamp) → per-frame pipeline:
│ ├── updateTransition(dt) → cubic ease interpolation across 30+ properties
│ ├── updateBlink(dt) → periodic blink cycle
│ ├── updateBreathing(state, dt) → sinusoidal breath offset
│ ├── updateSaccades(state, dt) → micro eye movements
│ └── Draw pipeline:
│ ├── drawBackground(dc) → gradient from mood bg color
│ ├── drawShadow(dc) → ellipse shadow under body
│ ├── drawTail(dc) → animated bezier tail
│ ├── drawBody(dc) → oval body with breathing
│ └── drawHead(dc) → orchestrates:
│ ├── drawEyes, drawNose, drawMouth
│ ├── drawWhiskers, drawBlush, drawBrows
│ └── drawParticles (sparkles, hearts, ?, !, zzz, sweat)
├── updateMousePosition(x, y) → gaze tracking
├── getState() → read-only state snapshot (dev tooling)
├── overrideProperty(key, value) → manual property override (dev tooling)
└── destroy() → cleanup RAF + resources
14 Moods Quick Reference
| Mood | Key Visual Features |
|---|---|
| neutral | Round eyes, slight smile, golden bg |
| focused | Narrow eyes, tight mouth, cool blue bg |
| cheerful | Happy squinted eyes, big smile, blush, warm bg |
| empathetic | Soft eyes, gentle smile, purple bg |
| curious | Wide eyes, raised brow, tilted head, teal bg |
| analytical | Narrow focused eyes, neutral mouth, steel bg |
| celebratory | Star eyes, huge smile, sparkles + bounce, pink bg |
| apologetic | Downcast eyes, frown, sweat drops, muted bg |
| inspired | Wide bright eyes, open mouth, sparkles, bright bg |
| urgent | Very wide eyes, open mouth, exclamation marks, orange bg |
| playful | Asymmetric brows, sly smile, bouncy body, fun bg |
| confused | Uneven eyes, wavy mouth, question marks, muted bg |
| proud | Confident eyes, smirk, chin up, warm golden bg |
| sleepy | Half-closed eyes, flat mouth, zzz particles, dark bg |