Iframe Sandbox Canvas Performance Impact Report #
Executive Summary #
When a web page runs inside an <iframe> with sandbox="allow-scripts allow-downloads" (objkt.com's default), the browser treats the content as having an "opaque origin" which triggers security checks on every canvas pixel operation. This causes approximately 50% FPS reduction and visual rendering issues.
Root Cause: Without allow-same-origin in the sandbox attribute, the iframe content is treated as cross-origin to itself, making the canvas "tainted" for security purposes.
Official Documentation Summary #
From WHATWG HTML Spec (html.spec.whatwg.org) #
-
Origin-Clean Flag: Every canvas bitmap has an
origin-cleanflag. When set tofalse, pixel-reading operations (getImageData,toDataURL,toBlob) throwSecurityError. -
Sandbox without
allow-same-origin:"If this token is not used, the resource is treated as being from a special origin that always fails the same-origin policy (potentially preventing access to data storage/cookies and some JavaScript APIs)."
-
Performance Impact on Canvas Operations:
willReadFrequentlyhint exists because "readback withgetImageData(),toDataURL(), ortoBlob()[is slower on GPU-accelerated canvases]"- When origin-clean checks must happen on every operation, the browser may disable GPU acceleration entirely
From MDN Web Docs #
-
getImageData()throws SecurityError if the canvas'sorigin-cleanflag isfalse(tainted canvas) -
Canvas Security Model:
"Information leakage can occur if scripts from one origin can access information (e.g. read pixels) from images from another origin"
Codebase Impact Analysis #
High-Impact Locations in bios.mjs #
| Line | Operation | Context | Sandbox Impact |
|---|---|---|---|
| 1477 | ctx.putImageData(imageData, 0, 0) |
EVERY FRAME render | ⚠️ Major - runs every animation frame |
| 1479 | ctx.getImageData(...) |
Fallback when buffer invalid | ⚠️ Moderate - only on resize/init |
| 1297 | parseCtx.getImageData(0, 0, 1, 1) |
coerceToRGB() color parsing |
⚠️ Low - fast path usually avoids this |
| 1319 | tempCtx.getImageData(sx, sy, 1, 1) |
Color sampling | ⚠️ Low - on-demand |
| 6460 | canvas.toBlob(resolve, "image/webp", 0.8) |
Screenshot/snap | 🔴 BLOCKED in sandbox |
| 6712 | canvas.toBlob(resolve, "image/webp", 0.9) |
Screenshot/snap | 🔴 BLOCKED in sandbox |
| 7115 | canvas.toBlob(resolve, "image/webp", 0.9) |
Screenshot/snap | 🔴 BLOCKED in sandbox |
| 7368 | canvas.toBlob(resolve, "image/png") |
Screenshot/snap | 🔴 BLOCKED in sandbox |
Operations That Will FAIL in Sandboxed Context #
These throw SecurityError when sandbox lacks allow-same-origin:
toDataURL()- Used for saving imagestoBlob()- Used for screenshot/snap features (already disabled in bundles)getImageData()when canvas has external content
Operations That SLOW DOWN in Sandboxed Context #
Even though they don't fail, these have significant overhead:
-
putImageData()- Every frame render (line 1477)- Browser must verify origin-clean status
- May disable GPU compositing path
- Forces software rendering fallback
-
Canvas pattern/gradient operations
- Cross-origin checks on fill styles
-
drawImage()from other canvases- Propagates taint status
Test Results (from our sandbox-perf-test.html) #
| Sandbox Mode | FPS | Memory | Visual Quality |
|---|---|---|---|
Restricted (allow-scripts allow-downloads) |
120 FPS | 59 MB | ❌ Rendering bugs |
With same-origin (allow-scripts allow-downloads allow-same-origin) |
239 FPS | 110 MB | ✅ Correct |
| No sandbox | 239 FPS | 110 MB | ✅ Correct |
Why objkt.com Default Mode is Slow #
objkt.com embeds NFT iframes with:
<iframe sandbox="allow-scripts allow-downloads" src="..."></iframe>
This is missing allow-same-origin, which causes:
- Content treated as opaque/cross-origin to itself
- Canvas security checks activate on every pixel operation
- Browser may fall back to software rendering
- ~50% FPS drop observed
Solution: Users must enable "Advanced Mode" on objkt.com, which adds allow-same-origin.
Affected KidLisp Features #
The $roz code uses these operations that suffer in sandboxed contexts:
| Feature | KidLisp Command | Canvas Operation | Impact |
|---|---|---|---|
| Render | (every frame) | putImageData |
⚠️ 50% slower |
| Scroll | scroll |
Reads/writes pixels | ⚠️ Slower |
| Contrast | contrast |
Pixel manipulation | ⚠️ Slower |
| Color parsing | ink, fade |
getImageData (fallback) |
⚠️ Potential slowdown |
Implemented Optimizations #
✅ Sandbox Detection (bios.mjs) #
Added early detection of opaque origin (sandboxed iframe without allow-same-origin):
// Detects objkt.com's sandbox="allow-scripts allow-downloads"
const isOpaqueOrigin = (() => {
try {
if (window.origin === 'null') return true;
localStorage.getItem('__sandbox_test__');
return false;
} catch (e) {
return true;
}
})();
// Exposed globally for pieces to detect
globalThis.acIsSandboxed = isOpaqueOrigin;
✅ Canvas Context Hints (bios.mjs) #
Added willReadFrequently: true hint to all canvas contexts when in sandboxed mode:
| Canvas | Line | Purpose |
|---|---|---|
ctx (main) |
~936 | Primary render target - already had hint |
uiCtx |
~1025 | UI overlay |
debugCtx |
~1029 | Debug overlay |
ffCtx |
~1036 | Freeze frame buffer |
octx |
~1041 | Corner label overlay |
dirtyBoxCtx |
~1045 | Dirty box updates |
glazeCompositeCtx |
~1061 | Glaze compositing |
coerceToRGB.ctx |
~1306 | Color parsing - already had hint |
This tells the browser to use software rendering from the start, avoiding the penalty of constant GPU→CPU security checks.
✅ Export Guards (bufferToBlob, captureFrame) #
Added early-return guards to functions that call toBlob/toDataURL:
bufferToBlob()in bios.mjs - Returnsnullwith warning in sandboxcaptureFrame()in frame-capture.mjs - Returnsnullin sandbox
Recommendations #
For objkt.com / NFT Platforms #
- Add
allow-same-originto sandbox attribute for art pieces - Or provide a performance toggle (like objkt's "Advanced Mode")
For KidLisp Bundles #
- ✅ Already done: Disabled
acPACK_MODE(screenshot features) - ✅ Already done: Disabled console auto-snaps
- ✅ Implemented: Detect sandboxed context and log warning
- Consider: Use
OffscreenCanvasfor heavy operations (when supported)
For Aesthetic Computer Runtime #
- ✅ Implemented: Detect opaque origin early
- ✅ Implemented: Skip impossible operations (
toBlob,toDataURL) - ✅ Implemented: Add
willReadFrequentlyhint to canvas contexts - Consider: Add FPS counter warning in sandbox mode
Files Changed #
| File | Changes |
|---|---|
| bios.mjs | Added isOpaqueOrigin detection, willReadFrequently hints, bufferToBlob guard |
| frame-capture.mjs | Added isOpaqueOrigin guard to captureFrame() |
References #
- WHATWG HTML Spec - Canvas Security
- MDN - getImageData() Security
- MDN - iframe sandbox attribute
- MDN - Same-origin policy
Report generated: January 27, 2026 Context: FF1 display device performance investigation for objkt.com tokens Updated: Added implementation details for sandbox detection and canvas optimizations