@axogeo/runtime
v0.1.2
Published
Embeddable axogeo graph player: the pure evaluation engine plus a React (@react-three/fiber) <AxoGeoPlayer>.
Maintainers
Readme
@axogeo/runtime
Embeddable AxoGeo graph player. Graph JSON in, live animated 3D out.
It runs the same evaluation engine as the AxoGeo editor, packaged with a React
(@react-three/fiber) <AxoGeoPlayer> component. No transpile, no baking: the
engine evaluates the graph per frame in the browser, so anything the editor can
play (particles, paths, age chains, keyframes) plays in the embed.
Install
npm install @axogeo/runtime three @react-three/fiberreact, three and @react-three/fiber are peer dependencies, so your copies
are used and three is never duplicated. Requires react 18 and fiber 8 (fiber 9
requires react 19).
Quick start
The player renders a WebGL canvas, so it is client-only. In Next.js:
'use client';
import dynamic from 'next/dynamic';
import { demoGraphs } from '@axogeo/runtime/demos';
const AxoGeoPlayer = dynamic(
() => import('@axogeo/runtime/player').then((m) => m.AxoGeoPlayer),
{ ssr: false }
);
export function HeroDemo() {
return (
<div style={{ width: '100%', height: 480 }}>
<AxoGeoPlayer graph={demoGraphs.island} autoRotate />
</div>
);
}The wrapper sizes the canvas — the player fills its parent, so a parent with no height renders nothing.
Entry points
import { AxoGeoPlayer } from '@axogeo/runtime/player' // player, no examples
import { demoGraphs } from '@axogeo/runtime/demos' // examples only
import { AxoGeoPlayer, demoGraphs } from '@axogeo/runtime' // bothPrefer /player in production. The examples are about 90 KB gzipped and the
build emits one module per entry, so importing from the root pulls them in
whether you use them or not: 96 KB gzipped for /player against 184 KB for the
root.
Props
| prop | default | notes |
|---|---|---|
| graph | required | graph JSON as the editor saves it |
| playing | true | pause/resume |
| frame | — | drive the clock yourself. When set, playing is ignored |
| mode | 'repeat' | 'repeat' \| 'pingpong' \| 'once' |
| fps | graph's | playback rate override |
| font | null | three Font for text nodes: const font = await loadFont() |
| camera | graph's | [x, y, z] position override |
| fit | false | frame the camera to the model, and size shadows to it |
| fitKey | — | change to re-frame, e.g. after a knob grows the model |
| controls | true | orbit controls |
| controlLimits | null | assigned onto OrbitControls: { enablePan, minDistance, maxDistance, maxPolarAngle } |
| autoRotate | false | slow turntable |
| frameloop | 'always' | 'demand' stops a static embed rendering every frame |
| background | transparent | canvas clear colour, e.g. '#111318' |
| style / className | — | wrapper div |
| onFrame | — | (frame) => void after each stepped frame |
| onFramed | — | ({ radius, dist, center }) => void after a fit |
| overrides | null | live param values, see below |
fit
Without it the camera stays wherever the graph stored it, and the shadow
frustum is a fixed box 10 units either side of the origin — anything further out
renders unlit. fit measures the model, frames it, and resizes the frustum to
match. Use fitKey to re-frame when the model changes size.
frameloop
'demand' renders only when something changes, which keeps a still embed from
running a WebGL loop on every page that holds one. It needs playing={false}
and autoRotate={false}; with either of those on it falls back to 'always',
since both need a continuous clock. A host-driven frame works fine with
'demand'.
Driving a graph from app state
overrides merges values into node params before evaluation, so any knob in the
editor can be driven from React state — sliders, game events, scroll position.
Changing an override does not restart the animation; the clock keeps running and
the change lands on the current frame.
const [height, setHeight] = useState(2);
const overrides = useMemo(() => ({ 'mygraph{+}value-171...': { value: height } }), [height]);
<AxoGeoPlayer graph={graph} overrides={overrides} />Memoize the object. A fresh identity rebuilds the graph on every render.
The editor's Export → React writes all of this for you: a standalone .jsx
component embedding the graph, with every Value node exposed as a typed prop
named from its label. Add a Value node, feed it into the graph, re-export, and
the component grows a prop.
Exports
| | |
|---|---|
| AxoGeoPlayer | the component |
| applyParamOverrides(graph, overrides) | the pure merge the player uses, for headless hosts |
| demoGraphs | 36 curated examples; Object.keys(demoGraphs) to list them |
| loadFont(url?), DEFAULT_FONT_URL | for graphs with text nodes |
| createPlayback | the clock (repeat/pingpong/once), if you step frames yourself |
| effectiveFrameloop | which render loop a given configuration can be served by |
| frameRoots, fitShadowCamera | the camera fit and shadow sizing, if you drive them yourself |
| createEngine, buildIR, registry, disposeObject | headless engine: evaluate any graph at any frame without React |
Headless
import { createEngine, buildIR, registry } from '@axogeo/runtime/player';
import { demoGraphs } from '@axogeo/runtime/demos';
const engine = createEngine();
const g = demoGraphs.fountain;
const ir = buildIR(
{
nodes: g.nodes,
edges: g.edges,
keyframes: g.keyframes || {},
animation: { fps: g.fps ?? 30, startFrame: g.startFrame ?? 0, endFrame: g.endFrame ?? 60 },
},
registry
);
const { output } = engine.evaluate(ir, 12); // a three.js Group at frame 12Performance
- Playback causes no React re-renders. Evaluation runs in
useFrameand the output swaps into the scene imperatively. - The engine caches per node, so static branches never re-evaluate during playback. GPU resources are reference-counted and disposed on swap.
- Lazy-load players below the fold. One canvas per visible tile is fine, but browsers cap the number of live WebGL contexts, so avoid mounting dozens.
- Use
frameloop="demand"for stills.
Licence
Proprietary. See LICENSE.
You can install it, use it commercially, and ship it inside your app, site or game, including serving the bundle to end users. You cannot republish it as a standalone library, or use it to build a competing procedural-geometry authoring or export tool.
This covers the player and the geometry engine bundled with it, not the AxoGeo editor or its export targets.
Bundled third-party code: three-subdivide (MIT), cdt2d (MIT).
Later versions may ship under more permissive terms.
Other terms: https://axogeo.com
