# 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.