Generated Computers
Locals는 Apple Silicon 엔진으로 로컬 LLM을 최대 3배 빠르게 실행하는 Generated Computers 제품
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A polyfill for the proposed HTML-in-Canvas API: put real, interactive HTML inside <canvas content="drawable"> and draw it with ctx.drawElementImage(), in today's desktop Chrome, Firefox and Safari, and on Chrome for Android and Safari for iOS (verified on two physical Android phones and a physical iPad, and on the Android Emulator and the iOS Simulator).
Live demo: giuseppescottolavina.github.io/html-in-canvas-polyfill · Try it in the browser: playground · Field test: check it on your device (about six minutes, one report file to send back; what it measures)
The plugin gallery with the water plugin: an ordinary HTML form inside a canvas, drawn through a WebGPU ripple effect. The click and the typing go to the real <input>. Recorded in headless Chrome (plugins/water/test/gallery.e2e.mjs --record). Seven more effects over the same form are one click away in the gallery; the smallest page that uses them is examples/effect-plugin.
HTML-in-Canvas lets a canvas, a WebGL scene or a WebGPU effect show live, accessible HTML: forms, text, UI. Chrome 157 ships it enabled by default (measured on 157.0.8103.39, without any flag), and so does Chrome Canary. This package provides the same API in Chrome before 157, Firefox 155+ and Safari 27+, so code written against the standard runs in those browsers too; in Chrome 157 it steps aside and the browser draws natively. It is native-first: wherever the browser implements the API in a usable form, it patches nothing of the standard surface and steps aside, and it never throws because a native build differs in a secondary member.
From npm, for a bundler (TypeScript declarations included):
npm install @giuseppe-scotto-lavina/html-in-canvas-polyfill
A new preview is published under the next tag first and moved to latest once it has been checked, so next (npm install …@next) can be one preview ahead.
import { install } from '@giuseppe-scotto-lavina/html-in-canvas-polyfill';
install(); // synchronous: call it before your code uses the API; throws HicUnsupportedError in a browser it refuses
Or from a CDN, with plain script tags placed before your own scripts (no defer, async or type="module": the polyfill installs while its tag runs):
<script src="https://cdn.jsdelivr.net/npm/@giuseppe-scotto-lavina/[email protected]/dist/hic-live-polyfill.iife.min.js"></script>
<!-- Firefox and Safari: this second bundle draws form controls in those engines' look. Chrome does not need it. -->
<script src="https://cdn.jsdelivr.net/npm/@giuseppe-scotto-lavina/[email protected]/dist/hic-live-engines.iife.min.js"></script>
To download nothing in a browser that implements the API (Chrome 157), load dist/hic-live-loader.iife.min.js instead (6 KB gzipped) and await HicLiveLoader.load() before your code uses the API; it fetches the core, and the engines bundle on Firefox and Safari, only where the polyfill runs. With a bundler, import { load } from '@giuseppe-scotto-lavina/html-in-canvas-polyfill/loader' does the same through import(), which the bundler must keep split (GETTING-STARTED).
Then use the standard API. This is the explainer's own example, unchanged:
<canvas style="width: 400px; height: 200px;" content="drawable">
<form drawable>
<label for="name">name:</label>
<input id="name">
</form>
</canvas>
<script>
const canvas = document.querySelector('canvas');
const form = document.querySelector('canvas form');
const ctx = canvas.getContext('2d');
canvas.onpaint = () => {
ctx.reset();
ctx.drawElementImage(form, 100, 0);
};
// Size the canvas grid to match the device scale factor.
new ResizeObserver((entries) => {
canvas.width = entries[0].devicePixelContentBoxSize[0].inlineSize;
canvas.height = entries[0].devicePixelContentBoxSize[0].blockSize;
}).observe(canvas, { box: 'device-pixel-content-box' });
</script>
content="drawable" makes the canvas lay out its children as real, live HTML. They stay invisible: the canvas shows only what you draw.drawable marks each element you will draw.paint event fires when the drawables have a current picture. Draw them there with ctx.drawElementImage(element, x, y) (x, y in canvas bitmap pixels). A draw before the first picture exists throws InvalidStateError, as natively.ResizeObserver keeps the canvas bitmap at device-pixel size, so the drawing is sharp.Put the script tags and the example in one HTML file and open it (from disk works too). Click the drawn field and type: the text goes into the real input, and the canvas shows it. Clicks reach an element where it is drawn (here 100 bitmap pixels to the right of its layout box). Step by step, with the examples: docs/guides/GETTING-STARTED.md. All bundles and their load order: docs/BUNDLES.md.
| browser | status |
|---|---|
| Chrome desktop 130 to 156 (no native API) | polyfill runs; measured on 154 and 155 (macOS); 156 and 157 (its native feature switched off) in CI (headless on Linux and Windows, headed on macOS: 11 of the 16 browser gates); 130 to 153 admitted but not verified |
| Chrome 157 and Chrome Canary (a native implementation, enabled by default: Chrome 157.0.8103.39 measured, Canary 157.0.8103.0 to 159.0.8137.0) | polyfill steps aside (mode native, kind complete, no bridge needed) and the page draws natively; the two extensions run over the native implementation (measured on Chrome 157.0.8103.39 and Canary 159.0.8137.0) |
Chrome 154-155 with the CanvasDrawElement flag (the earlier layoutsubtree draft) | used as found, with one console warning; load hic-live-native-bridge.iife.min.js before the core to bridge content="drawable" to it |
| Edge, Opera, Brave and other desktop Chromium 130+ | expected to work, not verified |
| Firefox 155+ desktop | polyfill runs; measured on 155 and 157 (macOS) |
| Safari 27+ desktop | polyfill runs; measured on 27.0.1 (macOS) |
| Safari on iOS / iPadOS 26+, other iOS browsers (WebKit) | polyfill runs with phone budgets and the iOS control look; verified on a physical iPad Pro 11-inch (iPadOS 27.2, 2026-10-30) and on the iOS Simulator (iOS 26.4, iPhone and iPad); not on a physical iPhone on iOS 26 or later |
| Chrome for Android 130+ | polyfill runs with phone budgets; verified on two physical phones, a Pixel 7 Pro (Android 17) and a Huawei Mate 10 Pro (Android 10), both Chrome 156, 2026-10-30, and on the Android Emulator with Chromium 158 (which ships HTML-in-Canvas natively: the polyfill steps aside there; its own path was forced for the test) |
| older Firefox and Safari, Chrome for Android < 130, iOS < 26, Android WebView, in-app iOS web views, Firefox for Android | refused with one console warning |
The headed browser gates run on macOS, at a device pixel ratio of 2 (some also at 1 or 1.25), and at 1, 1.25 and 1.5 against Chrome's native implementation. On Linux and Windows only headless Chrome runs, in CI (unit tests, bundle check, and the platform probe: fonts, form controls, scrollbars, DPR 1.25 / 1.5 and the text selection against native pixels). Nothing was measured on a physical Windows machine; physical phones and an iPad ran the mobile gate (docs/guides/MOBILE.md). Row-by-row evidence: docs/SUPPORT-MATRIX.md.
Short answers to the common questions (size, lag behind the DOM, accessibility, native support, privacy, what is verified): docs/guides/FAQ.md.
<hic-layer> element next to the canvas. children, parentNode and friends answer as if nothing moved, but CSS :last-child, :nth-child, + and live collections can still see the layer.<object> and <embed>, and cross-origin images, fonts and CSS without CORS. The browser itself leaves them out of a canvas; the polyfill draws the element's box without its content and logs one console message, never a silent gap. Also not drawn: native UI such as select popups, date pickers and IME composition. Same-origin iframes are drawn. A strict CSP must allow img-src data:.<img> plays where the browser has ImageDecoder (WebCodecs), in a secure context; Safari 27, which has none, shows its first frame. Animated CSS images (background-image and the like) show their first frame everywhere.<canvas content="drawable"> <hic-layer> next to the canvas, opacity 0, over its content box
<form drawable> ... ──moved──▶ the same live elements: layout, focus, typing, IME, scrolling, hit testing
│ mutation / input / scroll / resize observers report damage
▼
clone of the changed region ──▶ SVG foreignObject ──▶ data: URL ──▶ decode
│
▼
retained 2D surface per drawable (the old picture stays until the new one lands)
│
canvas.onpaint ──▶ ctx.drawElementImage(form, x, y) (also WebGL / WebGPU uploads and effect plugins)
The browser itself lays out and paints the HTML; the polyfill only takes pictures of it, so text, CSS and form controls look like the plain page. The main design choices and why they were made: docs/DESIGN-DECISIONS.md. Details: docs/ARCHITECTURE.md.
Measured on one machine (Apple M1 Max, macOS, 120 Hz, DPR 2) with the same pages, harness and input for all three. The other polyfill on npm implements the older layoutsubtree name, so it ran through a small attribute adapter (included).
| this polyfill | html-in-canvas-polyfill 1.0.1 | native (Chrome Canary) | |
|---|---|---|---|
| official WPT HTML-in-Canvas files passing | 214 of 248 (WPT of 2026-10-28) | 66 of 230 (WPT of 2026-10-07, not measured again) | 236 of 248 on Canary 159.0.8128.2, in each of its two full runs (WPT of 2026-10-28) |
| keydown to canvas pixel, p50 / p95 | 5.1 / 9.7 ms | 11.5 / 15.3 ms, final text wrong | 5.5 / 9.9 ms |
| click on a field until the canvas shows it focused, p50 / p95 | 14.6 / 15.4 ms | 7.7 / 23.8 ms | 6.8 / 7.7 ms |
| wheel scroll inside the canvas, p50 to pixel | 22.8 ms | never drawn | 37.8 ms |
| blank frames during real window resizes | 0 of 3135 | 542 of 3130 | 0 of 3127 |
| one update in a 1000-element drawable, main thread | 15.3 ms | 44.9 ms | 6.0 ms |
| first paint of a form page (new tab), p50 | 91 ms | 60 ms | 66 ms |
| core bundle, minified / gzipped | 356 KB / 121 KB | 30 KB / 10 KB | none |
| browsers | desktop Chrome before 157, Firefox 155+, Safari 27+; Chrome for Android 130+ (two physical phones and the emulator), iOS / iPadOS Safari 26+ (a physical iPad and the iPhone simulator) | claims all, including mobile (not verified here) | Chrome 157 and Canary (enabled by default) |
The WPT row is this polyfill on 2026-10-29 (docs/CONFORMANCE.md), the other package as measured on 2026-10-13, and native Chrome at the build whose copy of the tests is pinned (newer Canary builds already implement later API changes that the pinned tests predate); the latency, resize, update and first-paint rows of this polyfill were measured on 2026-10-31 with 0.1.0-preview.16 plus the two large-drawable fixes released in 0.1.0-preview.17 (Chrome 156), those of the other package and native on 2026-10-30/31 (Chrome 156; native: Canary 159.0.8131.0), each subject twice; the bundle row is the current build. The other package is smaller, starts faster and offers a Three.js renderer. Full tables, method and caveats: docs/PERFORMANCE-COMPARISON.md and docs/COMPARISON.md. Libraries such as html2canvas are a different tool: they re-implement CSS painting to take a static picture, and the result is not interactive.
Note. The frame cap and the effect plugins are extensions of this library. They are not part of the HTML-in-Canvas proposal, and browsers' native implementations do not provide them. They run over this polyfill and over a browser's own implementation that the polyfill steps aside for (what differs there).
<canvas content="drawable" framecap="30"> or canvas.frameCap = 60 bounds paint events, effect frames and snapshot builds for that canvas (0 = display rate).await handle.trigger('play') resolves with the state the plugin rests in). The plugins are not part of the polyfill: the water ripple of the demo is one example plugin in plugins/, with a template for new ones and a gallery that applies each to one live form; examples/effect-plugin is the smallest page that uses them. docs/guides/EFFECTS-PLUGINS.md is the reference for using and writing plugins.Eight example effect plugins, each applied to the same live form in the plugin gallery (a browser with WebGPU; measured on desktop Chrome and on Safari on an iPad). Under each of them the form stays live: a click is mapped through the effect to the element the user sees there (and swallowed where the effect shows no element), typing goes to the real field, and each plugin states what it does when the user prefers reduced motion. Seven have actions that page code runs with handle.trigger: disintegrate (out, in, play), shatter (impact, crack, out, in, play), glitch (burst), page-curl (peek), water (drop), and a demo on each of those and on holo-card and liquid-glass, a short showcase the gallery plays when the effect is selected. On a phone or a tablet a finger grabs the liquid glass or a page corner, and scrolls the page everywhere else; nothing renders at rest except what a plugin declares continuous: the CRT's flicker, the holo card's idle sway (30 frames a second) and glitch's ambient events (and the water's swell when its ambient option is above 0).
What each plugin does and how its input mapping works: plugins/README.md. Writing one: docs/guides/EFFECTS-PLUGINS.md.
npm run test:gates:all runs them with the WPT gate, the static checks, the unit tests and the mutation suite. In CI, the platform probe (G-platform) checks fonts, form controls, scrollbars, fractional DPR and text selection against native pixels on Linux, Windows and macOS.npm run test:mobile drives Mobile Safari in the iOS Simulator or on a physical iPhone or iPad over USB (--ios-device) and Chrome in the Android Emulator or on a physical Android phone over USB (--android-serial): taps on moved drawables, typing, touch scroll, composed scroll frames against the settled picture (iOS), the virtual keyboard, rotation, the iOS form-control look and the refusal of older browsers, each with a positive control.The gate list, oracles and thresholds are in docs/TEST-PLAN.md. CI runs everything that works headless on Linux and on Windows, and the browser gates (11 of the 16, with their controls), 34 of the 68 mutants, the example, site and framework suites and Firefox on GitHub-hosted macOS runners; what needs a 120 Hz display, a fast machine for throughput checks, continuous WebGPU frames, Safari 27, Chrome Canary's oracle for WPT or the phone simulators runs locally on macOS (npm run test:local; CONTRIBUTING.md).
The bundle files and console messages use the short name hic-live (HTML-in-canvas, live DOM).
Issues and pull requests are welcome; please read CONTRIBUTING.md first. For a bug, include the output of JSON.stringify(__HIC__.installed.report(), null, 1) from the page console (with the ES module, call getInstalled().report() from your code; troubleshooting). Security problems: SECURITY.md. This project follows the Code of Conduct.
MIT © 2026 Giuseppe Scotto Lavina
ElementImage sent through a MessagePort or BroadcastChannel (a dedicated worker that loads hic-live-worker takes one and draws it).0.1.0-preview.23 follows a proposed standard that may still change. The two extensions below may change between previews.