# Canvas and HTML layers Add scoped drawing and DOM to the existing Canvas renderer: ```ts const band = renderer.addLayer({ id: 'team-band', type: 'canvas', order: 'background', coordinates: 'world', draw(context, layer) { context.fillStyle = '#c4b5fd' context.fillRect(-40, -40, 240, 80) } }) band.disable() band.enable() band.dispose() ``` Canvas order is background → graph edges → underlay → nodes and labels → overlay. Registration order breaks ties. Screen coordinates (default) use CSS pixels; world coordinates include the camera transform and device pixel ratio. Canvas state is saved/restored and paths reset at each callback boundary, including errors. Custom Canvas drawings do not become graph pick targets. HTML layers own a scoped root above Canvas. Use DOM creation, not injected HTML: ```ts const annotation = renderer.addLayer({ id: 'annotation', type: 'html', coordinates: 'world', input: 'exclusive', mount(root, { signal, onCleanup, invalidate }) { const input = document.createElement('input') input.setAttribute('aria-label', 'Annotation') root.append(input) input.addEventListener('input', updateAnnotation, { signal }) onCleanup(subscribeToAnnotationChanges(invalidate)) }, update(root, { snapshot }) { // Update content or child world positions from the current visible snapshot. } }) ``` World roots use a camera CSS transform; screen roots remain unscaled. Position children within the root to anchor them to specific world points. The root itself is renderer-owned. HTML siblings follow registration order, including after re-enable. Pass-through input is the default; exclusive roots accept input within their content and stop bubbling pointer, click, wheel and keyboard events without preventing native controls. Application/global capture listeners remain app-owned. Do not override pass-through pointer behavior on descendants. Each enabled/mounted session starts on its first render. Canvas may provide an optional `mount(context)` for subscriptions/resources; HTML requires `mount(root, context)`. Mount receives a fresh AbortSignal and reverse-order `onCleanup` scope; a returned cleanup function is shorthand. Draw/update receives a read-only snapshot, viewport, coordinate conversions and `invalidate()` for external changes. Do not mutate graph data or invalidate unconditionally from drawing. Disable and unmount release session resources and remove HTML roots. Re-enable and remount start fresh sessions. Optional definition `dispose()` runs once at terminal disposal, including disabled layers. Failed mounts release partial resources and disable that layer. Cleanup failures do not strand sibling layers or the primary canvas. IDs are unique per renderer; a definition cannot be registered twice concurrently. These methods share the lifecycle vocabulary of [engine extensions](./extensions.md), while mounted DOM stays renderer-owned. The engine wires renderer invalidation into its coalesced redraw scheduler. Direct Canvas consumers receive a coalesced microtask redraw of the latest snapshot; submit an updated snapshot after changing its viewport. Unmount cancels queued direct redraws. HTML positioning temporarily makes a static host relative; only an unchanged renderer-owned inline value is restored during cleanup. Bitmap export includes Canvas layers. HTML remains excluded and its active layer IDs appear in `GraphImageExport.omittedLayerIds`. SVG support follows separately. Try [the layer example](../../vanilla/layers/index.html) for zoom, remount, input ownership, colored Canvas layers and export.