Draws SVG into the 3D scene as a real, depth-tested texture — an annotation that is part of the rendered image, so geometry in front of it truly covers it.
JavaScript API:
RasterizedAnnotationNode
This is the rasterized member of the AbstractDomAnnotationNode annotation
family: like every annotation its content is the element's own body and it tracks a
world position. What distinguishes it from the live and overlay annotations is that
its SVG body is rasterized to a GPU texture and composited inside the scene, so it
is genuinely depth-tested — occluded by geometry in front of it as a true 3D surface,
not a DOM layer over the canvas. This is the annotation to use when a label must sit
in the scene's depth (behind a passing object, on a surface) rather than on a plane
above everything.
With RenderableNode.overlay
enabled, the annotation instead renders on top of the composited scene with
depth-aware translucency: fragments occluded by scene geometry are dimmed
to RenderableNode.overlayOccludedOpacity rather than hidden — the
same node-level overlay semantics shared by all renderable nodes.
Rasterized annotations ARE interactive: the light-DOM body is retained as
an invisible hit-target, so pointer and drag events work exactly as they
do for live annotations (only the visible pixels come from the GPU
texture). What the rasterized pixels cannot do is animate via CSS — the
texture is a static snapshot until markSvgDirty schedules a
re-rasterization.
The body content lives in the host's light DOM. It is invisible (the host
is opacity: 0) but kept around for measurement and hit-testing — the
visible pixels come from the rasterized GPU texture.
Like live and overlay annotations, a rasterized annotation owns a shadow
projection wrapper (it extends AbstractWebAnnotationNode): the
light-DOM body slots through it, and the wrapper carries the framework's
anti-flash visibility and the pointer-events suppression gate. The host
stays opacity: 0 (DOM plumbing invisible, GPU texture visible); the
rasterizer still reads the light-DOM body markup via rasterizationContent,
so slotting does not change what gets rasterized.
After construction, mutate the content via standard DOM APIs
(annotation.innerHTML = ..., annotation.firstElementChild.querySelector(...))
and then call markSvgDirty to schedule re-rasterization.
Related. For live, CSS-animated, interactive HTML content layered over the canvas
(occludable), use sk-live-annotation; for content that must always stay on top
and never be hidden by geometry, use sk-overlay-annotation.
## Rasterization Limitations
The body content is handed to the platform unchanged — loaded as the
source of an <img> element (a blob URL with type image/svg+xml) and
drawn to a canvas. The platform is the sole decider of what renders.
Skenra does not inspect, validate, or rewrite the content. In practice
this means only valid, self-contained SVG rasterizes:
- Non-SVG content (HTML, e.g. <div>…</div>) loads but renders blank
with no error. Use LiveAnnotationNode for HTML.
- External references (<image href="https://…">, external fonts/CSS,
<use href> to other documents) render a broken-image glyph or blank
— the blob URL has no document base, so relative/cross-origin refs fail.
- **<foreignObject> and cross-origin images** taint the canvas; the
rasterizer then throws and the annotation logs a console.warn and
renders an empty quad (the scene continues).
- **<script>** is inert inside an <img>-loaded SVG (never executes).
- Markdown (md = true) is not yet rasterizable — the body is
Markdown source, not SVG, so it renders blank. (The md flag is
accepted for API uniformity across annotation types; a future
Markdown→SVG renderer would make it work.)
Malformed markup and canvas-tainting content surface as a console.warn
(naming the annotation id) plus an empty quad. Silently-blank cases
(HTML, external refs) are the platform's documented behavior and are not
warned. For best results: inline all styles, use rgb()/rgba() or named
colors, set explicit width/height and xmlns, and avoid external
references.
Security Warning: Content is NOT sanitized by this class. If accepting user-provided or untrusted content, you MUST sanitize it first to prevent XSS attacks.
Serve these docs to run the live example.
WebGPU needs a secure context — open this page via grunt serve
rather than double-clicking the file. The Markup tab works from disk.
<!-- A rasterized annotation renders its SVG to a texture composited INSIDE the
scene, so it is a true depth-tested surface: the cube passing in front of it
here genuinely covers it, the way it would cover any mesh. (That in-scene
compositing is the distinction from the DOM-layer live/overlay annotations.)
The SVG is the element BODY; `scale-with-distance` keeps a constant pixel
size when false. -->
<sk-scene>
<sk-plain-material id="mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="fg" base-color="#e0b050"></sk-plain-material>
<sk-sphere radius="1" segments="6"><sk-surface-paint material="#mat"></sk-surface-paint></sk-sphere>
<!-- A cube sweeping between the camera and the label, at the label's own
height — it passes IN FRONT of the annotation and covers it. -->
<sk-cube y="1.5" z="1.2" size="0.9">
<sk-surface-paint material="#fg"></sk-surface-paint>
<sk-animation duration="3000ms" iterations="Infinity" direction="alternate">
<sk-keyframe offset="0" x="-2.5"></sk-keyframe>
<sk-keyframe offset="1" x="2.5"></sk-keyframe>
</sk-animation>
</sk-cube>
<sk-rasterized-annotation y="1.5" anchor="center" scale-with-distance="false">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="44">
<rect x="1" y="1" width="118" height="42" rx="10"
fill="#1e1e1e" stroke="white" stroke-width="2"></rect>
<text x="60" y="29" text-anchor="middle" fill="white"
font-family="sans-serif" font-size="20">in-scene</text>
</svg>
</sk-rasterized-annotation>
</sk-scene>
| Attribute | Type | Default | Description |
|---|---|---|---|
id |
<id> |
— | The element's unique identifier — the standard HTML global id attribute. |
draggable |
<boolean> |
false |
Gets whether this node can be dragged. |
xanimatable |
<number> |
0 |
X position. |
yanimatable |
<number> |
0 |
Y position. |
zanimatable |
<number> |
0 |
Z position. |
hanimatable |
<angle> |
0 |
Heading (Y-axis rotation) in radians. |
panimatable |
<angle> |
0 |
Pitch (X-axis rotation) in radians. |
banimatable |
<angle> |
0 |
Bank (Z-axis rotation) in radians. |
quaternionanimatable |
<quaternion> ({ <number>, <number>, <number>, <number> }) |
— | Rotation as quaternion (overrides h, p, b if provided) |
sxanimatable |
<number> |
1 |
X-axis scale factor. |
syanimatable |
<number> |
1 |
Y-axis scale factor. |
szanimatable |
<number> |
1 |
Z-axis scale factor. |
s |
<number> |
— | Uniform scale factor for all axes (overrides sx, sy, sz if provided) write-only |
cast-shadow |
<boolean> |
— | Whether this annotation casts shadows. |
md |
<boolean> |
false |
When true, treat AbstractDomAnnotationConfig.content as |
anchor |
<anchor> ("center" | "top" | "bottom" | "left" | "right" | "top-left" | "top-right" | "bottom-left" | "bottom-right") |
"center" |
The anchor point for positioning the annotation. |
scale-with-distance |
<boolean> |
false |
If true, maintains constant screen size; if false, scales with perspective. |
overlay |
<boolean> |
false |
Whether this node renders as an overlay with depth-aware translucency. |
overlay-occluded-opacity |
<number> |
0.3 |
Alpha multiplier for overlay fragments occluded by scene geometry. |
Standard DOM event-handler content attributes — the value is JavaScript run when the event fires. They behave exactly as on any HTML element.
onclick, ondblclick, onauxclick, oncontextmenu, onpointerdown, onpointerup, onpointermove, onpointercancel, onpointerover, onpointerout, onpointerenter, onpointerleave, ongotpointercapture, onlostpointercapture, onwheel, ondragstart, ondrag, ondragenter, ondragover, ondragleave, ondrop, ondragend