ANARI C API and THREE.js Mapping
This page maps the official ANARI 1.1 specification to the experimental, private @luma.gl/anari implementation and, where helpful, to comparable THREE.js concepts.
The first column is the authoritative ANARI C vocabulary. The JavaScript column describes what this package actually implements, not what a fully conformant ANARI binding would need to implement. The THREE.js column is a conceptual migration aid, not an adapter or dependency.
@luma.gl/anari is not a binding to the ANARI C API, is not ABI-compatible with ANARI, and does not claim ANARI conformance. Some mappings are approximate, some are convenience extensions, and many official functions are not implemented.
High-level mental model
| Rendering concept | Official ANARI C | @luma.gl/anari | Comparable THREE.js concept |
|---|---|---|---|
| Rendering implementation | ANARILibrary + ANARIDevice | Existing luma.gl Device wrapped by ANARIDevice | WebGPURenderer or WebGLRenderer |
| Scene root | ANARIWorld | ANARIWorld | Scene |
| Geometry data | ANARIGeometry + ANARIArray | ANARIGeometry + ANARIArray | BufferGeometry, geometry subclasses, BufferAttribute |
| Surface appearance | ANARIMaterial | ANARIMaterial | MeshStandardMaterial, MeshPhysicalMaterial, or other material |
| Geometry + material | ANARISurface | ANARISurface | Mesh |
| Reusable collection | ANARIGroup | ANARIGroup | Group |
| Transformed placement | ANARIInstance | ANARIInstance | Object3D.matrix, Object3D.matrixWorld, or an InstancedMesh transform |
| Light | ANARILight | ANARILight | AmbientLight, DirectionalLight, PointLight, SpotLight |
| View | ANARICamera | ANARICamera | PerspectiveCamera or OrthographicCamera |
| Rendering policy | ANARIRenderer | ANARIRenderer | Renderer plus material, tone-mapping, and postprocessing configuration |
| Render operation | ANARIFrame + anariRenderFrame() | ANARIFrame.render() | renderer.render(scene, camera) |
THREE.js generally exposes a mutable, renderer-owned object graph. ANARI explicitly separates a device, retained parameters, committed scene objects, a renderer description, and frame operations. The two systems solve overlapping problems but do not have identical ownership or lifecycle semantics.
Library and device functions
| ANARI 1.1 C API | @luma.gl/anari | THREE.js comparison | Status and differences |
|---|---|---|---|
anariLoadLibrary() | No equivalent | Import three/webgpu or three | JavaScript modules are imported normally; no ANARI implementation library is dynamically loaded. |
anariUnloadLibrary() | No equivalent | No direct equivalent | Module loading and process lifetime are managed by the JavaScript runtime. |
anariNewDevice() | new ANARIDevice(graphicsDevice) | new WebGPURenderer() / new WebGLRenderer() | Wraps an already-created luma.gl device instead of creating an ANARI library-backed device. |
anariNewInitializedDevice() | Configure luma.createDevice(...), then new ANARIDevice(graphicsDevice) | Renderer constructor options | Comparable initialization step, but no ANARI initializer array or device subtype selection. |
anariGetDeviceSubtypes() | No equivalent | Renderer class / backend selection | Backend selection happens through luma.gl adapter ordering, not ANARI device-library discovery. |
anariGetDeviceExtensions() | anariDevice.extensions | Capability inspection on the selected renderer | Returns this proof of concept's static extension-name list; no library/subtype-specific extension query. |
ANARIStatusCallback | No equivalent | Application logging / error handling | No ANARI severity, status-code, callback, or callback-user-data interface. |
const graphicsDevice = await luma.createDevice({
adapters: [webgpuAdapter, webgl2Adapter],
createCanvasContext: true
});
const anariDevice = new ANARIDevice(graphicsDevice);
The native concept “select an ANARI device implementation” therefore maps to “select a luma.gl graphics backend, then wrap it,” not to loading a Khronos-compatible ANARI device.
Object creation functions
| ANARI 1.1 C API | @luma.gl/anari | Comparable THREE.js concept | Support |
|---|---|---|---|
anariNewArray1D(device, memory, deleter, userData, elementType, count) | anariDevice.newArray({data, elementType, dimensions}) | Typed array + BufferAttribute | Partial: one-dimensional typed arrays and object-reference arrays; no deleter callback or ownership transfer. |
anariNewArray2D() | No equivalent | DataTexture, texture image data | Not implemented. |
anariNewArray3D() | No equivalent | Data3DTexture | Not implemented. |
anariNewGeometry(device, subtype) | anariDevice.newGeometry(subtype, parameters) | BufferGeometry or a geometry subclass | Supported for triangle, sphere, cylinder, cone, and quad; primitive semantics are simplified. |
anariNewMaterial(device, subtype) | anariDevice.newMaterial(subtype, parameters) | MeshStandardMaterial / MeshPhysicalMaterial | Supported for matte and physicallyBased; many official material parameters are absent. |
anariNewSurface(device) | anariDevice.newSurface({geometry, material}) | new Mesh(geometry, material) | Supported; references are supplied in the factory call. |
anariNewGroup(device) | anariDevice.newGroup({surface, light}) | new Group() | Supported for surface/light collections. |
anariNewInstance(device, subtype) | anariDevice.newInstance({group, transform}) | Object3D transform or InstancedMesh.setMatrixAt() | Supported only for transform instances. |
anariNewWorld(device) | anariDevice.newWorld({surface, instance, light}) | new Scene() | Supported for direct surfaces, instances, and lights. |
anariNewLight(device, subtype) | anariDevice.newLight(subtype, parameters) | THREE.js light subclasses | Supported for directional, point, and spot; JavaScript additionally provides an ambient convenience subtype. |
anariNewCamera(device, subtype) | anariDevice.newCamera(subtype, parameters) | PerspectiveCamera / OrthographicCamera | Supported for perspective and orthographic. |
anariNewRenderer(device, subtype) | anariDevice.newRenderer(subtype, parameters) | Renderer configuration / debug material | Supported for default, WebGPU-only deferred and raytrace, debugNormals, debugDepth, and locally registered renderer runtimes. |
anariNewFrame(device) | anariDevice.newFrame({world, camera, renderer, size}) | renderer.render(scene, camera) / render target | Supported for canvas presentation; arbitrary mapped output channels are not implemented. |
anariNewSampler() | anariDevice.newSampler('image2D', {image, transform}) | Texture, sampler state, texture-backed material properties | Partial: retained 2D image samplers; no procedural or volume samplers. |
anariNewSpatialField() | No equivalent | 3D texture / volume field | Not implemented. |
anariNewVolume() | No equivalent | Volume renderer / 3D texture | Not implemented. |
anariNewObject() | new ANARIObject(...) exists; renderer runtimes register separately | Custom Object3D subclass | No generic extension-object registration; custom renderer runtimes use anariDevice.registerRenderer(). |
Important primitive differences
Official ANARI sphere, cylinder, cone, and quad geometries can represent collections of primitives described by arrays such as vertex.position, per-primitive indices, and radii. This implementation creates one procedural engine geometry from scalar parameters such as radius, height, width, and segments.
For example:
anariDevice.newGeometry('sphere', {radius: 1, segments: 32});
is conceptually closer to new THREE.SphereGeometry(1, ...) than to the full official ANARI sphere-soup data model. Use retained instances to place that procedural sphere repeatedly.
Official ANARI light subtypes include directional, point, spot, HDRI, quad, and ring lights. The JavaScript ambient light is a convenience extension; the standard expresses ambient illumination as renderer configuration such as ambientRadiance, rather than defining the same ambient light subtype.
Parameter and commit functions
| ANARI 1.1 C API | @luma.gl/anari | THREE.js comparison | Support and differences |
|---|---|---|---|
anariSetParameter(device, object, name, dataType, value) | object.setParameter(name, value) | Assign mesh.material.roughness = value | Supported conceptually. TypeScript and JavaScript values replace explicit ANARIDataType and C pointers. |
Repeated anariSetParameter(...) calls | object.setParameters({...}) | Assign several object/material properties | JavaScript convenience for staging multiple parameters. |
anariUnsetParameter(device, object, name) | object.unsetParameter(name) | Reset/delete a property, depending on the object | Supported; still requires commitParameters(). |
anariUnsetAllParameters() | No equivalent | Replace/reset object configuration | Not implemented. |
anariCommitParameters(device, object) | object.commitParameters() | Property updates, material.needsUpdate, attribute.needsUpdate, or updateMatrix() | Supported; staged changes become visible only after committing. |
| Read back a parameter | object.getParameter(name) / object.getParameters() | Read ordinary JavaScript object properties | JavaScript-only extension: official ANARI deliberately does not provide a general parameter-readback API. |
C versus JavaScript example
float roughness = 0.15f;
anariSetParameter(device, material, "roughness", ANARI_FLOAT32, &roughness);
anariCommitParameters(device, material);
material.setParameter('roughness', 0.15).commitParameters();
The commit boundary is intentionally similar. The parameter typing and ownership models are not: official ANARI passes an explicit data type and a pointer, while the JavaScript package uses typed method signatures and ordinary values.
Commit versus THREE.js updates
// @luma.gl/anari
material.setParameter('roughness', 0.15).commitParameters();
// Conceptual THREE.js equivalent
threeMaterial.roughness = 0.15;
THREE.js materials are generally mutated directly. Some operations additionally require explicit flags such as material.needsUpdate, attribute.needsUpdate, or instancedMesh.instanceMatrix.needsUpdate; those are GPU-update hints, not an ANARI-style transactional commit mechanism.
Arrays and mapped memory
| ANARI 1.1 C API | @luma.gl/anari | THREE.js comparison | Support |
|---|---|---|---|
anariNewArray1D() | newArray({data}) | new BufferAttribute(typedArray, itemSize) | Zero-copy JavaScript typed-array storage; no C deleter or reference-count contract. |
anariMapArray() | Access array.data directly | Access attribute.array | No explicit map call; retained typed-array data remains directly accessible. |
anariUnmapArray() | No equivalent | attribute.needsUpdate = true | No explicit map/unmap synchronization. Commit the owning geometry after changing rendered array data. |
anariMapParameterArray1D() | No equivalent | Allocate/update an attribute array | Not implemented. |
anariMapParameterArray2D() | No equivalent | Update texture image data | Not implemented. |
anariMapParameterArray3D() | No equivalent | Update 3D texture data | Not implemented. |
anariUnmapParameterArray() | No equivalent | attribute.needsUpdate / texture.needsUpdate | Not implemented. |
ANARIDeleterCallback | No equivalent | JavaScript garbage collection / explicit GPU dispose() | No application-memory deleter callback or transferred memory ownership. |
const positions = new Float32Array([-1, 0, 0, 1, 0, 0, 0, 1, 0]);
const array = anariDevice.newArray({data: positions, elementType: 'float32x3'});
positions[0] = -2;
geometry.commitParameters();
The array retains the original JavaScript object. array.length counts scalar JavaScript elements; new Float32Array(9) reports 9, not three vec3 elements.
Discovery, properties, and extensions
| ANARI 1.1 C API | @luma.gl/anari | THREE.js comparison | Support |
|---|---|---|---|
anariGetObjectSubtypes(device, objectType) | anariDevice.getObjectSubtypes(type) | Select known constructors / inspect renderer capabilities | Supported with JavaScript string object types. |
anariGetObjectInfo(device, objectType, subtype, infoName, infoType) | anariDevice.getObjectInfo(type) | Class/capability inspection | Partial: returns {type, subtypes, extensions} only, not arbitrary named metadata. |
anariGetParameterInfo() | No equivalent | Constructor documentation / TypeScript types | Not implemented; parameter schemas are documented and statically typed rather than introspectable at runtime. |
anariGetProperty() | No generic equivalent; use frame.statistics | Renderer/scene properties and renderer info | Partial only: render statistics are exposed directly, but ANARI property queries and wait masks are absent. |
anariGetDeviceExtensions() | anariDevice.extensions | Renderer/backend capability properties | Static package extension list rather than library-dependent/runtime-dependent discovery. |
anariDevice.getObjectSubtypes('geometry');
// ['triangle', 'sphere', 'cylinder', 'cone', 'quad']
anariDevice.getObjectInfo('material');
// {type: 'material', subtypes: [...], extensions: [...]}
The current extension names describe concepts the proof of concept supports; they are not evidence of complete, certified Khronos extension behavior.
Frame rendering and presentation
| ANARI 1.1 C API | @luma.gl/anari | THREE.js comparison | Support and differences |
|---|---|---|---|
anariRenderFrame(device, frame) | frame.render() or anariDevice.renderFrame(frame) | renderer.render(scene, camera) | Supported conceptually, but the JavaScript call immediately encodes/draws instead of exposing official asynchronous frame-operation semantics. |
anariFrameReady(device, frame, waitMask) | No equivalent | No exact equivalent | Not implemented; there is no polling/wait-mask frame API. |
anariDiscardFrame() | No equivalent | Stop an application animation loop | Not implemented; no in-flight frame cancellation API. |
anariMapFrame(device, frame, channel, ...) | No equivalent | Render target / pixel readback | Not implemented; frames present to the canvas and do not expose mapped pixel channels. |
anariUnmapFrame() | No equivalent | Release a mapped/readback resource | Not implemented. |
ANARIFrameCompletionCallback | No equivalent | Animation loop / promise integration | Not implemented. |
Frame channel.color, channel.depth, channel.normal, and other channels | No equivalent | Render targets, depth textures, G-buffers | Not implemented as ANARI channels. debugNormals and debugDepth are visualization renderers, not mappable output channels. |
const statistics = frame.render();
graphicsDevice.submit();
Official ANARI rendering is specified as asynchronous and may support readiness queries, cancellation, frame channels, mapping, and completion callbacks. The JavaScript proof of concept returns rendering statistics immediately while GPU execution still follows the underlying luma.gl device's command/submission behavior.
Retention and destruction
| ANARI 1.1 C API | @luma.gl/anari | THREE.js comparison | Support |
|---|---|---|---|
anariRetain() | No equivalent | Keep a JavaScript object reference | JavaScript object references replace explicit native handle retention. |
anariRelease() on a scene object | No equivalent | geometry.dispose() / material.dispose() | No general per-object reference-count or release method. |
anariRelease() on a frame | frame.destroy() | Dispose render targets / postprocessing resources | Releases frame-owned GPU resources, but is not a general ANARI handle-release operation. |
anariRelease() on a device | anariDevice.destroy() | renderer.dispose() | Releases ANARI runtime resources; does not destroy the separately owned luma.gl Device. |
frame.destroy();
anariDevice.destroy();
graphicsDevice.destroy();
Only destroy the graphics device when the application no longer shares it with other rendering or compute systems.
Geometry subtype comparison
| Official ANARI subtype | @luma.gl/anari | Comparable THREE.js class | Important difference |
|---|---|---|---|
triangle | newGeometry('triangle', {...}) | BufferGeometry + position/normal/tangent/UV/color/skin/index attributes | Supports positions, normals, XYZW tangents, RGB/RGBA colors, two UV sets, joint indices/weights, position/normal/tangent morph targets, and 16/32-bit indices; not the complete official attribute system. |
sphere | newGeometry('sphere', {radius, segments}) | SphereGeometry | One procedural sphere; official ANARI supports arrays of sphere primitives. |
cylinder | newGeometry('cylinder', {radius, height, segments}) | CylinderGeometry | One capped procedural cylinder; official ANARI supports collections of indexed cylinder primitives. |
cone | newGeometry('cone', {radius, height, segments}) | ConeGeometry | One capped procedural cone; official ANARI supports arrays of cone primitives. |
quad | newGeometry('quad', {width, height}) | PlaneGeometry | One XZ-plane procedural quad; official ANARI quad geometry supports explicit vertex/index arrays. |
curve | Not supported | Line, LineSegments, tube/curve geometry | Not implemented. |
isosurface | Not supported | Custom marching-cubes / isosurface implementation | Not implemented. |
Material parameter comparison
| Official ANARI concept | @luma.gl/anari | Comparable THREE.js property | Notes |
|---|---|---|---|
matte.color | material.color / baseColor / baseColorTexture | material.color / map | Constant RGB/RGBA values optionally multiplied by a retained image map. |
physicallyBased.baseColor | baseColor / baseColorTexture | MeshStandardMaterial.color / map | Constant color multiplied by an optional image sampler. |
metallic | metallic, metallicRoughnessTexture | MeshStandardMaterial.metalness / metalnessMap | Scalar metallic factor and the source map's blue channel. |
roughness | roughness, metallicRoughnessTexture | MeshStandardMaterial.roughness / roughnessMap | Scalar roughness factor and the source map's green channel. |
opacity | opacity, alphaMode, alphaCutoff | material.opacity + material.transparent / alphaTest | Committed structural changes rebuild the shared blend/mask rendering pipeline. |
alphaMode | opaque, mask, and blend; alphaCutoff supported | transparent, alphaTest | Committed changes select the appropriate shared rendering pipeline. |
emissive | emissive, emissiveTexture | MeshStandardMaterial.emissive / emissiveMap | Constant emissive RGB optionally multiplied by an image map. |
| Emissive scaling | emissiveStrength | MeshStandardMaterial.emissiveIntensity | JavaScript convenience scalar. |
clearcoat | clearcoat, clearcoatRoughness, clearcoatTexture, clearcoatRoughnessTexture, clearcoatNormalTexture | MeshPhysicalMaterial.clearcoat / clearcoat maps | Shared clearcoat term supports authored factor, roughness, and tangent-space normal maps. |
iridescence | iridescence, iridescenceTexture, iridescenceThicknessTexture | MeshPhysicalMaterial.iridescence / iridescence maps | Shared angle-dependent thin-film approximation with authored factor and thickness maps. |
| Normal, roughness, metallic, and base-color samplers | normalTexture, metallicRoughnessTexture, baseColorTexture | Material textures/maps | Retained image2D samplers support independent transforms and either primary or secondary UVs. |
| Transmission, index of refraction, and volume attenuation | transmission, transmissionTexture, thickness, thicknessTexture, attenuationColor, attenuationDistance, indexOfRefraction | MeshPhysicalMaterial.transmission / thickness / attenuation | Shared renderer captures opaque scene color for screen-space refraction; it is not layered ray tracing. |
| Sheen and anisotropy | sheenColor, sheenRoughness, sheenColorTexture, sheenRoughnessTexture, anisotropyStrength, anisotropyRotation, anisotropyTexture | MeshPhysicalMaterial.sheen / anisotropy and associated maps | Authored factors and maps are supported through the existing shared shader approximations. |
Light and camera comparison
| Official ANARI concept | @luma.gl/anari | Comparable THREE.js class/property | Notes |
|---|---|---|---|
| Directional light | newLight('directional', {direction, irradiance}) | DirectionalLight | Direction and intensity map conceptually; shadow behavior is absent. |
| Point light | newLight('point', {position, intensity}) | PointLight | Fixed attenuation; no official radius/power behavior. |
| Spot light | newLight('spot', {position, direction, openingAngle, falloffAngle}) | SpotLight.angle / SpotLight.penumbra | openingAngle sets the outer cone; falloffAngle sets the inner cone. |
| Renderer ambient lighting | renderer.ambientRadiance | AmbientLight or environment lighting | This aligns more closely with the official renderer ambient-light extension. |
| Ambient light object | newLight('ambient', ...) | AmbientLight | JavaScript convenience; not an official ANARI 1.1 light subtype. |
| HDRI / quad / ring area lights | Not supported | Environment maps / area lights | Not implemented. |
| Perspective camera | newCamera('perspective', {fovy, ...}) | PerspectiveCamera | ANARI-style fovy is expressed in radians; THREE.js constructor fov uses degrees. |
| Orthographic camera | newCamera('orthographic', {height, ...}) | OrthographicCamera | JavaScript derives horizontal extent from height * aspect. |
| Camera depth of field, motion blur, stereo, panoramic projections | Not supported | Specialized camera / postprocessing features | Not implemented. |
THREE.js migration example
The following snippets express approximately the same simple scene. They are not interchangeable implementations.
THREE.js
import * as THREE from 'three';
const scene = new THREE.Scene();
const geometry = new THREE.SphereGeometry(1, 48, 24);
const material = new THREE.MeshPhysicalMaterial({
color: 0x3388ff,
metalness: 0.85,
roughness: 0.18,
clearcoat: 0.25
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 1, 0);
scene.add(mesh);
const light = new THREE.PointLight(0xff8833, 30);
light.position.set(3, 2, 0);
scene.add(light);
const camera = new THREE.PerspectiveCamera(50, width / height, 0.05, 200);
camera.position.set(0, 2, 8);
camera.lookAt(0, 1, 0);
renderer.render(scene, camera);
@luma.gl/anari
const geometry = anariDevice.newGeometry('sphere', {radius: 1, segments: 24});
const material = anariDevice.newMaterial('physicallyBased', {
baseColor: [0.2, 0.53, 1],
metallic: 0.85,
roughness: 0.18,
clearcoat: 0.25
});
const surface = anariDevice.newSurface({geometry, material});
const group = anariDevice.newGroup({surface: [surface]});
const instance = anariDevice.newInstance({
group,
transform: new Matrix4().translate([0, 1, 0])
});
const light = anariDevice.newLight('point', {
position: [3, 2, 0],
color: [1, 0.53, 0.2],
intensity: 30
});
const world = anariDevice.newWorld({instance: [instance], light: [light]});
const camera = anariDevice.newCamera('perspective', {
position: [0, 2, 8],
direction: [0, -1, -8],
fovy: 50 * Math.PI / 180
});
const renderer = anariDevice.newRenderer('default');
const frame = anariDevice.newFrame({world, camera, renderer, size: [width, height]});
frame.render();
THREE.js-specific differences
- THREE.js attaches transforms directly to
Object3D; ANARI separates surfaces, groups, and transform instances. - THREE.js does not require a general explicit parameter commit; ANARI-style updates require
commitParameters(). - THREE.js
InstancedMeshis constructed explicitly and requires instance-matrix updates; this package derives instanced draws from sharedANARISurfaceidentities. - THREE.js
MeshStandardMaterial.metalnesscorresponds conceptually to ANARImetallic, not to a property namedmetalnessin this package. - THREE.js camera field of view is commonly specified in degrees; ANARI
fovyhere is specified in radians. - THREE.js provides many mature features absent from this proof of concept, including extensive texture/material systems, shadow maps, loaders, raycasting, postprocessing, and broad scenegraph functionality.
- THREE.js
WebGPURenderercan fall back to WebGL 2; this package obtains similar portability by configuring luma.gl WebGPU and WebGL adapters explicitly.