Skip to content

justdom-three ​

A reactive Three.js scene graph, built on justdom-signal with the same operator vocabulary as justdom-core — $, set, children, effects, on — except targeting THREE.Object3D instead of DOM nodes. No VDOM, no diffing: every reactive value is wired directly to a Three.js mutation through a signal effect.

npm install justdom-three justdom-signal three

three and justdom-signal are peer dependencies. The package has three entry points: justdom-three for the scene, justdom-three/page for the two functions a widget on a page asks of a portal, which import nothing from three.js, and justdom-three/svg for the SVG plugin.

This reference is in six pages. Each section says what a part of the package does, lists its options with their defaults, and, where the part mirrors the page, ends with what it follows of the page and what it does not. The reasoning behind the choices is in the source headers the sections name. The guide is the place to start; this is the place to look things up.

PageWhat is in it
Rendering and sessionsrender() and its options, the context getters, useFrame, createXRButton and its parts.
Pointing, grabbing and handsinteractions() and makeInteractive(), the events a ray sends, cursors(), mouse(), drag(), carry(), controllers().
DOM panelsdom() and createPortal(), what a ray sends the page, mirroring, what a panel follows, loading and the budget, drag and drop, haptics, stamps, checkboxes, text fields and the keyboard, placing a panel, panel defaults, element plugins.
What a panel drawsLists, transforms, stylesheets, animation, revealed text, paint order, images, gradients, clipping and scrolling, SVG: the CSS a panel follows and the CSS it does not.
Development toolsTheming mode, outlines mode.

What is left over is below: the exports not covered by a section of their own, and where to see the package at work.

Also exported ​

Named here so that nothing public goes unmentioned; each has its JSDoc.

  • fragment(...sources) mounts several components as one. copy(key, value) writes a subscribable's values into a property that has a copy method, a Vector3 or a Color, the way set assigns a plain one. layoutEffects(fn) runs an effect once the object is in the scene, and not again when attach moves it. list and keyedList are the keyed lists justdom-core has, for Three objects, and Nothing() is a component that mounts nothing.
  • size(ctx) is the renderer's size as a source. InteractionContext and InteractionManager are the manager behind interactions(ctx), for a test or a scene that drives one by hand. setLocalMatrix and setWorldMatrix put an object at a matrix without allocating.
  • PortalPluginsContext, PanelOptionsContext and PortalSchedulerContext are the properties render() sets from portalPlugins, panelOptions and portalBudget, for a scene built without render(); createPortalScheduler and createFrameShareBudget are what it installs there.
  • getContentGroup(object) is where a node's projected children hang, and where a plugin attaches its own. isTextInput(node) is the rule the portal draws a field by. solveCut(layout, cursor) is the arithmetic behind makeReveal.
  • From justdom-three/page: getPortalRoot and scrollElementBy again, the two functions a widget on a page asks of a portal, and FOCUS_ATTRIBUTE, HOVER_ATTRIBUTE and ACTIVE_ATTRIBUTE, the names of data-xr-focus, data-xr-hover and data-xr-active, and followUserActions for a page that sets them with no panel (What a panel draws), from an entry point that imports nothing from three.js. A widget a page shows before any session, and a panel may mirror during one, imports them from there, so that the page's own bundle does not carry the renderer; the dropdown and the scrolling list in justdom-three-ui do. The package declares no side effects, so a bundler that shakes trees also leaves the renderer out of a page that imports the same names from justdom-three. A dev server does not bundle, though, and loads every module the main entry names, which is why the page's entry stays the one to import from.
  • From justdom-three/svg: shape, group, image and drawText are the builders svgElements() dispatches to, for a plugin that draws one SVG element its own way and hands the rest to them.

Real examples ​

This package drives every demo in this repository — apps/castle, apps/desk, apps/minesweeper, apps/todo and the rest — worth reading for realistic scenes combining lighting, keyed lists (list/keyedList), controller/hand input, and DOM panels projected into 3D space. apps/showcase is the sampler: a deck of panels, one on show at a time, each on one area of what a panel draws. Boxes covers radii, borders, opacity, an overflow: hidden corner and a layout-tier width transition; backgrounds covers linear, radial, conic and repeating gradients, url() pictures sized, placed and tiled, and the two stacks a panel draws differently from a page; before & after covers ::before and ::after in flow, as a content: url() picture, positioned, lifted by z-index and restyled on :hover; paint order covers a positioned box over later flow, a transformed one over its siblings, a translucent card over a label and a nested overlay menu; text covers weights, spacing, shadows, animated colour and a paragraph revealed to a moving cursor; lists covers bullets and numbers that arrive as steps are added; images covers every object-fit; vectors is a card of inline SVG (an icon sprite of <symbol>s drawn by <use>, a pipeline diagram with marker heads, dashed and dotted edges and <text> labels, a chart clipped to its plot by a clip-path, icons recoloured on hover, <image> and four animations); SVG paint covers gradients and patterns (both units, a focus off centre, gradientTransform, href inheritance, the three spread methods, patternTransform and a box-sized pattern); motion covers CSS keyframes, a Web Animations API animation and a useFrame(ctx, fn) loop writing inline styles; pointer covers hover and press through :hover and :active; forms covers checkboxes styled by :checked and data-xr-checked, the next sibling included, and text fields that open the scene's keyboard and wear :focus while it types; widgets covers justdom-three-ui's dropdown and scrollable, a list pulled by hand, drag and drop between two lists, and a draggable panel's carry strips. ?panel=<id> opens it on one panel, and Prev and Next beside the XR button step through the deck without a session. ?panel=all puts every panel in the scene at once, in a grid, which is what the paint-order benches in packages/three count. ?panels=texture has the browser draw the panel on show with HTML-in-Canvas instead, as the reference the mirror is compared against. apps/desk is the same features in one scene, and mirrors a page that already exists rather than building panels for the scene: a desk whose monitor reveals its program, whose keys are pressed with the ray, whose floppies are dragged from one panel onto another, with a layer menu, live SVG charts, draggable cards, a text field with the scene's keyboard and a memo that can be picked up and tipped to read.

Four of the apps carry a check script, tests/xr-check.mjs, that plays the app on an emulated headset in headless Chromium through justdom-xr/driver: apps/minesweeper, apps/desk, apps/euclid and apps/knights. Each app's README says what its check does. They are run by hand; the repository's CI does not run them.