Every Skenra scene is a tree of Custom Elements inside an <sk-scene>. This guide starts from defaults and builds up a lit, animated, interactive scene one concept at a time — a cube and a sphere on a shadow-catching ground plane, with your own light and camera, an annotated callout, and a click interaction. The live example above is the finished result; each step below shows the scene as it stands, with the newly added markup highlighted.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-camera-orbit-control fit-targets="auto" fit-margin="0.15"
azimuth="35deg" elevation="26deg" orient-toward-pivot></sk-camera-orbit-control>
<sk-perspective-camera focal-length="25"></sk-perspective-camera>
<sk-directional-light intensity="1">
<sk-orbit-constraint radius="6" elevation="40deg" orientation-axis="+z" orient-toward-pivot>
<sk-animation duration="12000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" azimuth="0deg"></sk-keyframe>
<sk-keyframe offset="1" azimuth="360deg"></sk-keyframe>
</sk-animation>
</sk-orbit-constraint>
</sk-directional-light>
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a">
<sk-animation duration="4000ms" iterations="Infinity" direction="alternate" easing="ease-in-out">
<sk-keyframe offset="0" base-color="#d8a24a"></sk-keyframe>
<sk-keyframe offset="1" base-color="#4ad8a2"></sk-keyframe>
</sk-animation>
</sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plain-material id="callout-mat" base-color="#e8e8e8"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube id="cube" x="-1.6" size="1.8" style="cursor: pointer;"
onclick="const p = this.querySelector('sk-surface-paint'); p.setAttribute('material', p.getAttribute('material') === 'cube-mat' ? 'sphere-mat' : 'cube-mat');">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
<sk-animation duration="6000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" b="0deg"></sk-keyframe>
<sk-keyframe offset="1" b="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
<!-- The callout (annotation + elbow-connector spline) added in the
previous step, unchanged. -->
<sk-spline z="2">
<sk-svg-spline-source coordinate-space="xy"
path="M -3.6,2 L -3.6,0.6 Q -3.6,0 -3.0,0 L -2.2,0 Q -1.6,0 -1.6,-0.6 L -1.6,-1.2"></sk-svg-spline-source>
<sk-stroke-paint material="#callout-mat" width="1"></sk-stroke-paint>
</sk-spline>
<sk-live-annotation x="-3.6" y="2" z="2" anchor="bottom">
<div style="background: rgba(30,30,30,0.85); color: white; padding: 4px 10px;
border-radius: 8px; font: 13px sans-serif; white-space: nowrap;">Move your cursor and click!</div>
</sk-live-annotation>
</sk-scene>
Author an <sk-scene> and drop three meshes in it. Materials are scene resources declared once and referenced by id from a paint. With no camera or light declared, the scene supplies sensible defaults — the shapes are auto-fitted, lit, and interactive out of the box (drag to orbit, scroll to zoom), and the ground plane catches a shadow.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
</sk-scene>
A node can carry more than one paint. Add an <sk-edge-paint> alongside the surface paint to stroke the cube’s edges. Edge paint references its own material, so the outline colour is independent of the fill.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a"></sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
</sk-scene>
Declaring an <sk-directional-light> replaces the scene’s default light. A directional light is defined by its direction, set with the pitch (p) and heading (h) rotation attributes — here p="90deg" points it straight down (rotation attributes take a CSS angle like "90deg" or "1.57rad"); intensity scales it.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a"></sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-directional-light intensity="1" p="90deg"></sk-directional-light>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
</sk-scene>
Declaring an <sk-perspective-camera> replaces the default camera. Place it directly with the transform attributes — here y="10" with p="-90deg" looks straight down for a top view — though the next step shows an easier way to frame and orbit the scene.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a"></sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-perspective-camera focal-length="25" y="10" x="0" z="0" p="-90deg"></sk-perspective-camera>
<sk-directional-light intensity="1" p="90deg"></sk-directional-light>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
</sk-scene>
Scenes are interactive out of the box, but declaring a scene-root <sk-camera-orbit-control> lets you customize the controller: auto-frame the content (fit-targets="auto"), set the starting angle (azimuth/elevation), and tune the drag-to-orbit, scroll-to-zoom, right-drag-to-pan feel. The camera itself becomes bare; do not also add an orbit constraint to the camera.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-camera-orbit-control fit-targets="auto" fit-margin="0.15"
azimuth="35deg" elevation="26deg" orient-toward-pivot></sk-camera-orbit-control>
<sk-perspective-camera focal-length="25"></sk-perspective-camera>
<sk-directional-light intensity="1" p="90deg"></sk-directional-light>
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a"></sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
</sk-scene>
An <sk-orbit-constraint> positions its host on a sphere around a pivot (radius / azimuth / elevation). With orient-toward-pivot it also aims the host at the pivot — so the directional light’s direction follows the orbit, not just its position.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-camera-orbit-control fit-targets="auto" fit-margin="0.15"
azimuth="35deg" elevation="26deg" orient-toward-pivot></sk-camera-orbit-control>
<sk-perspective-camera focal-length="25"></sk-perspective-camera>
<sk-directional-light intensity="1">
<sk-orbit-constraint radius="6" elevation="40deg" orientation-axis="+z" orient-toward-pivot></sk-orbit-constraint>
</sk-directional-light>
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a"></sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
</sk-scene>
Animation is declarative: nest an <sk-animation> inside the node (or material) to animate, with <sk-keyframe> children naming the property at each offset. Material properties (base-color) animate inside the material; transform properties (the roll rotation b) inside the node.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-camera-orbit-control fit-targets="auto" fit-margin="0.15"
azimuth="35deg" elevation="26deg" orient-toward-pivot></sk-camera-orbit-control>
<sk-perspective-camera focal-length="25"></sk-perspective-camera>
<sk-directional-light intensity="1">
<sk-orbit-constraint radius="6" elevation="40deg" orientation-axis="+z" orient-toward-pivot></sk-orbit-constraint>
</sk-directional-light>
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a">
<sk-animation duration="4000ms" iterations="Infinity" direction="alternate" easing="ease-in-out">
<sk-keyframe offset="0" base-color="#d8a24a"></sk-keyframe>
<sk-keyframe offset="1" base-color="#4ad8a2"></sk-keyframe>
</sk-animation>
</sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
<sk-animation duration="6000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" b="0deg"></sk-keyframe>
<sk-keyframe offset="1" b="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
</sk-scene>
The orbit constraint exposes azimuth as an animatable property, so you animate the light’s orbit by animating the constraint — sweep azimuth 0deg → 360deg for a full revolution of the sun around the scene.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-camera-orbit-control fit-targets="auto" fit-margin="0.15"
azimuth="35deg" elevation="26deg" orient-toward-pivot></sk-camera-orbit-control>
<sk-perspective-camera focal-length="25"></sk-perspective-camera>
<sk-directional-light intensity="1">
<sk-orbit-constraint radius="6" elevation="40deg" orientation-axis="+z" orient-toward-pivot>
<sk-animation duration="12000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" azimuth="0deg"></sk-keyframe>
<sk-keyframe offset="1" azimuth="360deg"></sk-keyframe>
</sk-animation>
</sk-orbit-constraint>
</sk-directional-light>
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a">
<sk-animation duration="4000ms" iterations="Infinity" direction="alternate" easing="ease-in-out">
<sk-keyframe offset="0" base-color="#d8a24a"></sk-keyframe>
<sk-keyframe offset="1" base-color="#4ad8a2"></sk-keyframe>
</sk-animation>
</sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
<sk-animation duration="6000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" b="0deg"></sk-keyframe>
<sk-keyframe offset="1" b="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
</sk-scene>
Point at the cube with a callout built from two elements. An <sk-live-annotation> is an HTML label anchored to a 3D point — here (-3.6, 2, 2), with anchor="bottom" so the label sits above its anchor; its content is ordinary HTML in the element’s body. An <sk-spline> draws the connector: its path is an SVG path string laid in a plane chosen by coordinate-space. Using "xy" puts it in the Skenra X/Y plane with Y up (no inversion), so the path coordinates match the world positions directly — it runs from the label’s anchor straight down, around two rounded elbows, to the cube’s base. The spline is stroked with an <sk-stroke-paint> (width is in screen pixels by default).
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-camera-orbit-control fit-targets="auto" fit-margin="0.15"
azimuth="35deg" elevation="26deg" orient-toward-pivot></sk-camera-orbit-control>
<sk-perspective-camera focal-length="25"></sk-perspective-camera>
<sk-directional-light intensity="1">
<sk-orbit-constraint radius="6" elevation="40deg" orientation-axis="+z" orient-toward-pivot>
<sk-animation duration="12000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" azimuth="0deg"></sk-keyframe>
<sk-keyframe offset="1" azimuth="360deg"></sk-keyframe>
</sk-animation>
</sk-orbit-constraint>
</sk-directional-light>
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a">
<sk-animation duration="4000ms" iterations="Infinity" direction="alternate" easing="ease-in-out">
<sk-keyframe offset="0" base-color="#d8a24a"></sk-keyframe>
<sk-keyframe offset="1" base-color="#4ad8a2"></sk-keyframe>
</sk-animation>
</sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plain-material id="callout-mat" base-color="#e8e8e8"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube x="-1.6" size="1.8">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
<sk-animation duration="6000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" b="0deg"></sk-keyframe>
<sk-keyframe offset="1" b="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
<!-- Callout: an <sk-live-annotation> HTML label anchored in 3D at
(-3.6, 2, 2), wired to the cube base (-1.6, -1.2, 2) by an
elbow-connector <sk-spline>. Its <sk-svg-spline-source> child path is
authored in the Skenra Y-up
XY plane (coordinate-space="xy", no inversion), so its coordinates
match the world anchor/target directly; the node sits at z="2". -->
<sk-spline z="2">
<sk-svg-spline-source coordinate-space="xy"
path="M -3.6,2 L -3.6,0.6 Q -3.6,0 -3.0,0 L -2.2,0 Q -1.6,0 -1.6,-0.6 L -1.6,-1.2"></sk-svg-spline-source>
<sk-stroke-paint material="#callout-mat" width="1"></sk-stroke-paint>
</sk-spline>
<sk-live-annotation x="-3.6" y="2" z="2" anchor="bottom">
<div style="background: rgba(30,30,30,0.85); color: white; padding: 4px 10px;
border-radius: 8px; font: 13px sans-serif; white-space: nowrap;">Move your cursor and click!</div>
</sk-live-annotation>
</sk-scene>
Scene elements are real DOM elements, so standard event-handler attributes work — and standard CSS too: style="cursor: pointer" hints the cube is clickable. Clicking it toggles its surface paint between the cube and sphere materials — changing a paint’s material="…" at runtime re-renders it. With the callout inviting the interaction, this is the finished scene shown at the top.
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.
<sk-scene style="width: 100%; height: 420px;">
<sk-camera-orbit-control fit-targets="auto" fit-margin="0.15"
azimuth="35deg" elevation="26deg" orient-toward-pivot></sk-camera-orbit-control>
<sk-perspective-camera focal-length="25"></sk-perspective-camera>
<sk-directional-light intensity="1">
<sk-orbit-constraint radius="6" elevation="40deg" orientation-axis="+z" orient-toward-pivot>
<sk-animation duration="12000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" azimuth="0deg"></sk-keyframe>
<sk-keyframe offset="1" azimuth="360deg"></sk-keyframe>
</sk-animation>
</sk-orbit-constraint>
</sk-directional-light>
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#7fa8d8"></sk-plain-material>
<sk-plain-material id="sphere-mat" base-color="#d8a24a">
<sk-animation duration="4000ms" iterations="Infinity" direction="alternate" easing="ease-in-out">
<sk-keyframe offset="0" base-color="#d8a24a"></sk-keyframe>
<sk-keyframe offset="1" base-color="#4ad8a2"></sk-keyframe>
</sk-animation>
</sk-plain-material>
<sk-plain-material id="wire-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plain-material id="callout-mat" base-color="#e8e8e8"></sk-plain-material>
<sk-plane y="-1.2" width="12" height="12" orientation="+y" cull-mode="none">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
</sk-plane>
<sk-cube id="cube" x="-1.6" size="1.8" style="cursor: pointer;"
onclick="const p = this.querySelector('sk-surface-paint'); p.setAttribute('material', p.getAttribute('material') === 'cube-mat' ? 'sphere-mat' : 'cube-mat');">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-edge-paint material="#wire-mat"></sk-edge-paint>
<sk-animation duration="6000ms" iterations="Infinity" easing="linear">
<sk-keyframe offset="0" b="0deg"></sk-keyframe>
<sk-keyframe offset="1" b="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
<sk-sphere x="1.6" radius="1.1" segments="48">
<sk-surface-paint material="#sphere-mat"></sk-surface-paint>
</sk-sphere>
<!-- The callout (annotation + elbow-connector spline) added in the
previous step, unchanged. -->
<sk-spline z="2">
<sk-svg-spline-source coordinate-space="xy"
path="M -3.6,2 L -3.6,0.6 Q -3.6,0 -3.0,0 L -2.2,0 Q -1.6,0 -1.6,-0.6 L -1.6,-1.2"></sk-svg-spline-source>
<sk-stroke-paint material="#callout-mat" width="1"></sk-stroke-paint>
</sk-spline>
<sk-live-annotation x="-3.6" y="2" z="2" anchor="bottom">
<div style="background: rgba(30,30,30,0.85); color: white; padding: 4px 10px;
border-radius: 8px; font: 13px sans-serif; white-space: nowrap;">Move your cursor and click!</div>
</sk-live-annotation>
</sk-scene>
Elements used: <sk-animation>, <sk-camera-orbit-control>, <sk-cube>, <sk-directional-light>, <sk-edge-paint>, <sk-keyframe>, <sk-live-annotation>, <sk-orbit-constraint>, <sk-perspective-camera>, <sk-plain-material>, <sk-plane>, <sk-scene>, <sk-sphere>, <sk-spline>, <sk-stroke-paint>, <sk-surface-paint>, <sk-svg-spline-source>
Classes: CameraOrbitControl, Cube, DirectionalLight, EdgePaint, LiveAnnotationNode, OrbitConstraint, PerspectiveCamera, PlainMaterial, Plane, SVGSplineSource, Scene, SkenraAnimation, SkenraKeyframe, Sphere, Spline, StrokePaint, SurfacePaint