Skip to main content

API Summary

This page summarizes the public API exposed by the Genies Web SDK, organized by area. Named exports are canonical — initialize() also returns a NafInstance containing the most common APIs.

What is NAF?

NAF (Native Avatar Framework) is a WebGL SDK for loading, animating, and rendering Genies avatars in the browser. It wraps a C++ animation engine compiled to WebAssembly (via Emscripten) and exposes a clean JavaScript API on top of Three.js.

Source of truth

TypeScript definitions ship with the package and are the source of truth for exact types, overloads, and optional fields — your editor resolves them automatically from @geniesinc/genies-naf-webgl. This page focuses on consumer-facing APIs; some lower-level runtime hooks exist in the package for tooling and custom integrations, but are not part of the recommended app integration surface.

Quick start

import { initialize } from '@geniesinc/genies-naf-webgl';

const naf = await initialize({
canvas,
initScene: () => ({ renderer, scene, camera }),
environment: 'prod',
bearerToken: token,
});

const avatar = await naf.loadAvatarProgressive(avatarSource, {
renderer,
scene,
lod: [2, 0],
idle: { id: 'idle_neutral', version: '1' },
});

function frame(deltaTime) {
avatar.controller?.update(deltaTime);
avatar.controller?.syncPose();
naf.update(deltaTime);
}

// Releases native, Three.js, animation, shader, and LOD resources.
avatar.unload();

Choose a loading API

GoalAPI
Load an avatar at one LODloadAvatar(source, options)
Load silhouette, progressive LODs, animation, and behaviorloadAvatarProgressive(source, options)
Load a wearable without an avatarloadWearable(id, options)
Warm the complete avatar cachepreloadAssets(source, lods?)
Warm individual assetsprefetchAsset(...) / prefetchAssets(...)
Build a custom rendering integrationadvanced.rendering

A source is either an avatar definition or a CAMP avatar ID such as bm_avatar_*. CAMP (Content and Asset Management Platform) is the collection of Genies backend services and infrastructure. Pass a CAMP ID and the SDK resolves the underlying assets for you. A bm_smart-avatar_* ID must first be passed to resolveSmartAvatar() — then load the avatarId it returns.

CAMP ID prefixes

CAMP avatars, wearables, animations, smart avatars, and behaviors support bm_, vm_, and um_ prefixes. The bm_ examples on this page also accept the other prefixes. Always pass the full ID unchanged — prefixes are part of asset identity, not interchangeable aliases.

Avatar Definition

Avatar definitions are JSONs that describe an avatar's assets:

{
"assets": [
{ "id": "asset-abc-123", "version": "1" },
{ "id": "asset-def-456", "version": "1" }
],
"shapeAttributes": { ... }
}

Initialization and runtime

initialize(options?)

Initializes WASM, the virtual filesystem, resolver configuration, texture configuration, authentication, and the managed rendering frontend. It is idempotent and returns a Promise<NafInstance>.

OptionPurpose
canvasExisting canvas. The SDK creates one when omitted.
width, heightSize of an SDK-created canvas.
initScene(canvas, gl)Return { renderer, scene, camera?, render? } to register the Web render host.
environmentAsset environment: 'dev' or 'prod'.
bearerTokenGlobal native REST bearer token. Empty enables native authentication fallback.
servicesPer-service { baseUrl?, bearerToken?, sdkVersion? } overrides.
assetResolverConfigPathCustom resolver configuration URL.
textureSettingsPathCustom texture settings URL.
maxSupportedPipelineVersionHighest supported asset pipeline version.
managedRenderFrontendDefaults to true. Set false only for a consumer-owned renderer.
WebGL context optionsalpha, antialias, depth, stencil, preserveDrawingBuffer, powerPreference, premultipliedAlpha, desynchronized.

Runtime APIs

APIPurpose
update(deltaTime)Process native frame state, texture uploads, managed rendering, and first-frame work. Call once per frame.
getCanvas() / getContext()Return the SDK canvas and WebGL2 context.
setWasmBasePath(path)Override the auto-detected WASM asset path before initialization.
setEnvironment(env) / getEnvironment()Change or read the resolver environment.
BUILD_CONFIGRead { buildType, isDebug, isRelease }.
NafInstanceConvenience facade returned by initialize(). Named exports remain canonical.
Changed in v0.1.96

update(deltaTime) no longer accepts a renderer — managed WebGL state reset and frame submission now happen internally. Drop the second argument when upgrading.

Authentication and services

The SDK supports two authentication modes:

  • External auth — you manage the token and hand it to the SDK.
  • Native auth — the SDK manages everything automatically (used when the token is empty).

Prefer configuring auth at startup via initialize({ bearerToken, services }). The setters below are intended for runtime changes, such as a token refresh.

APIPurpose
setBearerToken(token)Set the global native REST token.
setServiceBaseUrl(service, url)Override a service endpoint; an empty URL clears it.
setServiceBearerToken(service, token)Override a service token; null or empty clears it.
setServiceSdkVersion(service, version)Override a service compatibility version; empty restores its configuration default.
startNativeSseStream(options)Open a native-owned SSE stream. Returns { stop(), state() }, or null if RTC bindings are unavailable.
import { setBearerToken } from '@geniesinc/genies-naf-webgl';

// Update when your token refreshes
onTokenRefresh((newToken) => {
setBearerToken(newToken);
});

// Clear to switch back to native auth
setBearerToken('');
CAMP service version

Set services.camp.sdkVersion to select the CAMP compatibility version for avatar, wearable, animation, smart-avatar, and behavior requests. An explicit per-request sdkVersion takes precedence without changing that default; otherwise native service configuration supplies the version. This is the service compatibility version — independent of the installed Web SDK package version.


Avatars and wearables

Loading

APIResult
loadAvatar(source, options)Promise<NafAvatarHandle> at one LOD.
loadAvatarProgressive(source, options)Promise<NafAvatarHandle> with optional silhouette, LOD progression, animation, and behavior.
loadWearable(id, options)Promise<NafWearableHandle> for a CAMP wearable. CAMP IDs are self-versioned — the version is part of the ID, so you don't pass one.
loadWearable(id, version, options)Promise<NafWearableHandle> for a legacy (pre-CAMP) wearable, where the version is a separate argument.
getLoadedAvatars()Avatar handles currently registered by the runtime. Check isReady before driving one. Unloaded handles are removed.
pickAvatar(raycaster, options?)Closest ready avatar intersection, or null.

Common load options:

OptionPurpose
rendererRequired THREE.WebGLRenderer.
lodOne LOD for loadAvatar; an order such as [2, 0] for progressive loading.
meshOptionsMesh construction options.
proceduralTexturesProcedural textures are generated on the GPU as the avatar loads. Defaults to true; disable to use supported authored fallbacks.
dynamicsEmbedded dynamics by default, an override, or false to disable.
signalCancels stale loads and suppresses late publication.

Progressive-only options:

OptionPurpose
sceneLets the SDK add/remove silhouette and final meshes without a visual gap.
position, rotationInitial transform shared by silhouette and final avatar.
idleThe looping idle animation the avatar plays when it isn't doing anything else, given as { id, version? }id is the animation asset's identifier and version is only needed for legacy IDs. Providing it creates and configures the animation controller for you.
behaviorSmart-avatar behavior — the configuration that decides which idle, gesture, and talking clips play and when. Requires animation. See the field table below.
syncOptionsSkeleton, blendshape, and coordinate-conversion synchronization options.
onSilhouette, onReady, onLodUpgrade, onErrorPer-load callbacks. The silhouette is a fast, low-detail stand-in mesh shown while the full-quality avatar streams in, so there's no empty space on screen — onSilhouette fires when it's ready. onReady fires after animation setup but before the final mesh is added to the scene, and is awaited, which makes it the right place to apply shaders and presets.
note

Native cache resolution may skip requested lower-quality LODs. The avatar:ready event reports the actual starting LOD as initialLod.

The behavior object:

FieldPurpose
configA behavior configuration you already have in hand, as an object or JSON string. Using it skips the network fetch.
behaviorIdA CAMP behavior-config ID (bm_behavior-config_*) for the SDK to fetch.
slugThe legacy (pre-CAMP) way to identify a behavior for fetching.
talkingAnimation asset ID used for the talking motion.
breathingAnimation asset ID used for the breathing motion.
emotionDefault emotion clip, used when the behavior's demeanor doesn't specify one.
onLoadedFires once the behavior and all its clips have finished loading.

If more than one source is supplied, precedence is config first, then behaviorId, then slug.

NafAvatarHandle

The handle owns the complete avatar lifecycle. Its mesh identity remains stable across supported updates.

MemberPurpose
avatarId, mesh, controllerRuntime identity, Three.js group, and optional animation controller.
status, isReady, isUnloadedLifecycle state. Do not drive a handle before isReady.
currentLodLOD currently rendered, or null.
proceduralTexturesEnabledEffective procedural texture mode.
userDataA free-form object for your own metadata. The SDK never reads or writes it, so you can attach whatever your app needs (a database key, UI state) and it travels with the handle.
getEquippedAssets()Assets retained after native composition rules.
getSourceShaderPreset()Independent copy of the base Mega Stylizer preset plus current asset presets under multiAsset, or null.
getExtensionNames() / getExtensionJson(name)Inspect the JSON extensions baked into the avatar's assets — extra authored data attached to the asset, such as GENIES_dynamics (physics setup) or GENIES_rig_recipe (skeleton setup). getExtensionNames() lists which are present; getExtensionJson(name) returns one as a JSON string.
equipAsset(...) / unequipAsset(id)Add or remove a wearable and publish the resulting update.
replaceAssets(ids, replacement...)Atomically remove and optionally replace assets.
setColor(id, color, options?) / unsetColor(...)Apply or remove native runtime color overrides.
getColorAttributes()Return global and per-asset color IDs.
configureDynamics(...)Re-resolve the effective dynamics source.
setDynamicsOverride(...) / clearDynamicsOverride()Apply or clear a local dynamics override.
getDynamicsState()Return { source, active, error, overrideError }.
getRefitReport() / getAnchorReport()Refit and anchoring diagnostics.
getSkeletonRootCorrectionReport()Report inverse-bind-matrix root correction.
getUtilityMeshData()Return copied utility mesh geometry for tooling.
unload()Idempotently release all resources owned by the handle.
Ordering

Equip, unequip, and replace mutations are serialized per handle. Await each operation when order matters, since remote resolution can finish out of order. Progressive wearable upgrades replace only the affected render data and keep animation state continuous.

Refitting and layering run during native composition using the asset's authored rest-cage and/or anchor bindings — they do not require a separate JavaScript call. Use getRefitReport() and getAnchorReport() to inspect the active composition.

NafWearableHandle

Standalone wearables let you load and mutate a wearable without a full avatar. They expose a stable mesh, renderId, lifecycle state, userData, asset mutation, runtime color, getSourceShaderPreset(), and unload(). Source presets use the same stable asset scopes as avatars.

Standalone wearables do not have an avatar skeleton, animation controller, dynamics, or base-rig restriction.


Animation and behavior

Top-level APIs

APIPurpose
attachAnimation(handle, options?)Create an AvatarAnimationController, attach it to the handle, and apply dynamics.
loadAnimation(id, version?)Load a legacy resolver animation or CAMP bm_anim-asset_* clip as a Uint8Array; returns null when unavailable.
categorizeClips(names)Split names into { idleClips, gestureClips }.
activateBehavior(controller, options?)Activate behavior from config, CAMP behaviorId, or legacy slug. Returns Promise<boolean>.
resolveSmartAvatar(id, options?)Resolve bm_smart-avatar_* to id, displayName, avatarId, behaviorId, and expanded avatar, behavior, brain, and voice objects.
resolveBehavior(id, options?)Resolve bm_behavior-config_* to an editable config accepted by activateBehavior.
AvatarAnimationControllerPublic controller class used by the high-level loaders.

activateBehavior accepts config, behaviorId, slug, talking, breathing, emotion, transferIdleTimeFrom, onLoaded, avatarId, signal, and isCurrent. Source precedence is config, then behaviorId, then slug. CAMP resolution uses the configured CAMP service credentials and rejects HTTP or invalid-content failures. Both resolve APIs accept an optional sdkVersion. The same behavior options work with loadAvatarProgressive, including after equip/unequip rebuilds.

Knowing when behavior is really loaded

Use onLoaded or the avatar:behavior-loaded event to observe native behavior loading completion — the activation result alone does not signal that all clips are loaded.

Working with CAMP smart avatars: resolve the smart-avatar ID first, pass its avatarId to loadAvatarProgressive, and set behavior: { behaviorId: resolved.behaviorId }. Brain and voice data are returned for inspection only — they are not automatically activated. An unassigned brain or voice comes back as null; avatar and behavior are required. Native code converts CAMP animation lists/tags into the controller's editable behavior format and downloads their OZZ clips through the shared asset cache.

AvatarAnimationController

GroupAPIs
Lifecycleupdate(dt), syncPose(), dispose(), hasAnimation, isPaused, isDisposed, blinkEnabled
Load clipsloadClip(source, version?), loadClipFromAssetResolver(...), loadClipFromBuffer(...)
PlaybacksetIdle, playGesture, setTalking, setEmotion, playEmotionBody, playGestureTag, playEmotion, playGUID
TimegetIdleTime, setIdleTime, getChannelInfo, getChannelCurrentTime, setChannelCurrentTime
WeightssetIdleWeight, setTalkingWeight, setTalkingEnabled, setEmotionWeight, setBoredomWeight, setGestureWeight, setBreathWeight, setAutoBoredom
Weight read-backgetChannelWeights, getTalkingWeight, getLookWeight, getGazeWeight
RetargetingsetAllRetargetEnabled, getAllRetargetEnabled, setRetargetEnabled, getRetargetEnabled
Look and gazesetLookWeight, setGazeWeight, getHeadBone, setLookAtCamera, setAttentionTemplate
BehaviorloadSmartAvatarBehavior, loadSmartAvatarBehaviorFromConfig, getBehaviorConfig, setBehaviorConfig, getAvailableAnimTags
Graph toolingserializeGraph, applyGraphParams
Current stategetCurrentBoredGUID, getCurrentSecondaryGUID, getCurrentGestureGUID, getCurrentEmotion
VoicesetVoiceAnimation, getVoiceAnimationTime, setVoiceAnimationTime, getTalkingActive, stopVoiceAnimation

Channel names are idle, boredom, gesture, breath, baseEmotion, emote1 through emote4, talking, and secondary. Use getChannelCurrentTime() for low-allocation frame polling.

Loading clips. controller.loadClip(source, version?) accepts a local OZZ path, a legacy AnimationLibrary/... ID, or a CAMP bm_anim-asset_* ID, and returns an AnimationClip. loadAnimation(id, version?) accepts either resolver ID format and returns the raw bytes instead. The optional version applies only to legacy IDs. CAMP loading resolves runtimeOzz internally — you don't need resolver documents or artifact URLs.

Playing gestures. Use playGesture(clip, options?) for a loaded clip. After behavior loading, getAvailableAnimTags() and playGestureTag(tag, onComplete?, startTime?) work with both legacy and CAMP behavior sources (startTime is in seconds). Note that loading an individual clip does not register behavior tags. The lower-level loadSmartAvatarBehavior(slug, ...) remains the legacy slug loader — use activateBehavior(controller, { behaviorId }) for a CAMP behavior ID.

Clip cleanup

There is no public per-clip removal API. Controller disposal releases its clip handles, while shared native clip data may remain cached for reuse. An avatar's unload() disposes its controller automatically.

Retargeting can be enabled globally or per body channel: idle, boredom, secondary, gesture, and breath. Source height, coordinate space, and motion descriptors are baked clip metadata and remain read-only.

Dynamics, IK, and body motion

Dynamics is physics-based secondary motion — hair, cloth, and accessories that sway and settle as the avatar moves. Its configuration ships with the asset in the GENIES_dynamics extension.

A rig recipe is the companion setup for the avatar's skeleton: it declares how the rig is assembled and corrected — which joints drive which, how IK is wired, and which twist corrections to apply. It ships with the asset in the GENIES_rig_recipe extension.

Body motion is procedural idle movement — the small, continuous adjustments that keep a standing avatar from looking frozen. It is driven by four sub-configs: balance (shifting weight between the feet over time), posture (spine bend and how open the clavicles, elbows, knees, and legs sit), micro (small fidgets), and gaze (looking around, including how much the torso follows). It runs together with full-body IK (FBIK), so setFBIKEnabled(false) turns procedural body motion off as well.

Dynamics and rig recipes are applied automatically by the high-level avatar flow — no consumer call is required. The direct controller APIs below exist for runtime tools:

AreaAPIs
DynamicsappendDynamicsGroup, clearDynamics, resetSimulation, setDynamicsEnabled, setDynamicsWeight, getDynamicsWeight, getLastDynamicsGroupError
Rig recipesetRigRecipeConfig, getRigRecipeConfig, getLastRigRecipeConfigError
IKsetFBIKEnabled, getFBIKEnabled
Body motion configsetBodyMotionConfig, getBodyMotionConfig, getLastBodyMotionConfigError, getBodyMotionDiagnostics
Body motion commandsplayBodyMotionGesture, setBodyMotionBalanceLibrary, forceBodyMotionSide, forceBodyMotionStance, forceBodyMotionFidget, getLastBodyMotionCommandError

BodyMotionConfig is a partial merge — it merges into the active configuration rather than replacing it. A balance library, by contrast, is replaced as one coherent graph. Check the diagnostics and last-error APIs after runtime edits.

DynamicsConfigLoader and RigRecipeConfigLoader can fetch or apply external JSON directly, for when automatic extension resolution isn't what you want.

Skeleton fixups. Rig recipes also accept skeletonFixups: an array of { helper, target, weight, kind: 'twistY' } bindings. Each entry overrides one of the built-in default corrections, matched by its helper joint. (Those defaults are named for the standard Genies rig generation, Gen14 — most integrators never need to reference them directly.) Helper and target names you supply are validated against the avatar's actual skeleton, and any default joint the skeleton doesn't have is skipped. Inside the native animation graph, secondary dynamics runs before these twist corrections, and emotion mouth closing runs after voice animation.

await avatar.setDynamicsOverride({ config: editedDynamics });
avatar.controller.setRigRecipeConfig(editedRecipe);
avatar.controller.setFBIKEnabled(true);

Managed rendering

By default the SDK owns rendering for you: native code publishes render models, pose frames, scene state, visibility, and render-pass plans, and the Web frontend translates the approved work into Three.js/WebGL and presents it. Most apps never need to touch this — configure the scene through the APIs below, or opt out entirely with managedRenderFrontend: false plus advanced.rendering.

APIPurpose
configureRenderer(config, options?)Apply a partial scene configuration and return status, diagnostics, and effective state.
getRendererCapabilities()Return backend features, limits, and sample counts.
getRendererConfiguration()Return the effective scene state and revision.
setRendererTexture(slot, texture)Bind or clear a Three.js scene texture. Returns true; invalid input or backend rejection throws.
getRendererMetrics() / resetRendererMetrics()Read or reset native render publication and logical-memory metrics.
RendererFallbackPolicySTRICT or DISABLE_UNSUPPORTED.
RendererTextureSlotBACKGROUND, ENVIRONMENT, or COLOR_GRADING_LUT.

Configuration sections are camera, directionalLights, environment, shadows, outline, silhouette, and postProcess. Omitted sections retain their current state. In strict mode unsupported requests are rejected; in disable-unsupported mode the supported subset is applied and the changes are reported.

Reading the metrics correctly

getRendererMetrics().currentLogicalModelBytes measures logical render-model buffers — not total WASM heap, refitting working memory, or GPU allocation bytes.


Materials and shaders

getShaderManager() returns the SDK singleton. Avatar loading, LOD changes, rebuilds, and unloads automatically attach, refresh, and detach shader state.

APIPurpose
getShaderManager()Access the active avatar's toon, outline, preset, and material controls.
mergeMegaStylizerPresets(layers)Merge ordered preset layers without applying them.
manager.applyShaderPreset(preset)Replace the active avatar's effective preset.
manager.applyShaderPresetLayers(layers)Merge layers and apply the result once.
manager.setActiveAvatar(id)Select the avatar affected by subsequent controls.
manager.setActiveMesh(name, materialIndex?)Scope controls to one mesh or material; pass null to clear.
manager.getMeshNames() / getMeshMaterials()Inspect available scopes; material entries include sourceMeshIds and sourceAssetIds.
manager.setRgbaMaskColors(params)Update RGBA/RGBK mask colors for the active scope (see RGBK colors).
manager.toon / manager.outlineTyped effect-specific controls.
manager.dispose()Dispose all managed effect state.

Built-in effects

Two effects are built in and controlled through the manager: Toon (cel / stylized shading) and Outline (a drawn outline around the avatar). You can adjust either live with individual setter calls, or describe a whole look at once with a JSON preset.

Together these make up the Mega Stylizer — the SDK's stylized shading system. A Mega Stylizer preset is a JSON document describing an avatar's complete authored look: toon shading values, outline settings, and surface state such as transparency.

Toon (manager.toon) — stylized cel shading. Supports typed setParam / getParam, preset, material-model, and scoped read-back APIs. A few parameters you'll reach for most:

  • cartoonMix blends between realistic and toon shading: 0 is fully lit (realistic), 1 is fully toon, and values in between are a hybrid.
  • aoAlbedoDarken darkens the base color in ambient-occlusion areas — crevices and contact shadows — for a more hand-painted look. 0 leaves the color untouched; higher values deepen it.
  • useOrmAlpha (01) applies to glTF materials only; it's ignored (forced to 0) on legacy Unity-URP materials.

Outline (manager.outline) — draws an outline around the avatar (an inverted-hull outline). Its controls fall into three groups:

  • Appearance: enable, thickness, color, alpha, width, depth bias, and resolution.
  • Silhouette gating: setSilhouetteGate(v), setGateGeomMix(v), setGateStrictness(v).
  • Scope + read-back: setActiveMesh(name), getMeshNames(), getMeshParams(name?).

Silhouette gating restricts the outline to the model's silhouette — its contour (rim) edges — instead of drawing it across flat, front-facing surfaces. setSilhouetteGate(0) disables gating so the outline is drawn everywhere; any value above 0 turns it on and trims the non-silhouette parts. In a preset, the matching fields are outlineSilhouetteGate, outlineGateGeomMix, and outlineGateStrictness.

Full parameter set

Refer to MegaStylizerParameters, MegaStylizerPreset, and ShaderManager in the type declarations for every available parameter.

Presets are JSON

A preset is a plain JSON object describing the look you want, which you hand to applyShaderPreset(). Presets support global properties plus three scoping maps:

  • multiMesh — keyed by mesh target.
  • multiMaterial — keyed by material name.
  • multiAsset — keyed by stable public asset ID. Each entry holds a complete preset, including its own mesh and material overrides, and affects only materials belonging to that asset — not the whole avatar.

Surface state — transparency and culling. These optional fields control how a material draws:

  • alphaModeOPAQUE, MASK (a hard cutout using alphaCutoff), or BLEND (soft transparency).
  • alphaCutoff — the threshold used by MASK.
  • opacity — overall opacity. Setting opacity on its own does not turn on transparency; use alphaMode: "BLEND" for that.
  • doubleSided — render both faces of a surface instead of culling the back.
  • depthWrite — whether the material writes to the depth buffer. BLEND defaults this to false.

For example — make everything a hard cutout, but let the hair blend softly:

{
"alphaMode": "MASK",
"alphaCutoff": 0.5,
"doubleSided": true,
"opacity": 1,
"depthWrite": true,
"useOrmAlpha": 0,
"multiMaterial": {
"HairMaterial": {
"alphaMode": "BLEND",
"opacity": 0.8,
"depthWrite": false,
"useOrmAlpha": 1
}
}
}

For convenience — and to keep older presets working — a few fields accept alternate names that map onto the canonical field:

  • alphaOpacityopacity
  • twoSideddoubleSided
  • alphaEnabledalphaMode: false becomes OPAQUE; true becomes MASK when a positive alphaCutoff is set, otherwise BLEND.

Using presets with TypeScript. The SDK ships types so your editor can autocomplete preset fields and catch typos before you run. Import MegaStylizerPreset for the JSON shape accepted by applyShaderPreset(), or MegaStylizerParameters for the canonical toon.setParam() values. Applying satisfies MegaStylizerPreset gives you full IntelliSense and rejects unknown fields:

import { getShaderManager, type MegaStylizerPreset } from '@geniesinc/genies-naf-webgl';

const preset = {
cartoonMix: 0.8,
albedoSaturation: 1.1,
alphaMode: 'MASK',
alphaCutoff: 0.5,
outlineEnabled: true,
outlineSilhouetteGate: 0.2,
multiMaterial: {
HairMaterial: { alphaMode: 'BLEND', opacity: 0.8, depthWrite: false }
}
} satisfies MegaStylizerPreset;

getShaderManager().applyShaderPreset(preset);

Preset layers and asset scopes

Layers let you stack presets in order — for example, keep an avatar's authored look as the base and apply a wearable's overrides on top. Merge rules for mergeMegaStylizerPresets() and applyShaderPresetLayers():

  1. Layers are applied from lowest to highest priority.
  2. Later values replace earlier values, including arrays and non-scoped objects.
  3. multiMesh, multiMaterial, and multiAsset merge recursively by target/property.
  4. null removes an inherited property, target, or map; undefined is ignored.
  5. Inputs are never mutated; results are independent copies.

For a matching material, runtime precedence from lowest to highest is: avatar-global, avatar multiMesh, asset-global, asset multiMesh, avatar multiMaterial, then asset multiMaterial.

The SDK normalizes source shaderPresets.megaStylizer, preferring it over the legacy shaderPresets.toonParams alias. Loading applies the source preset; successful equip, unequip, and replacement update the asset scopes automatically, and failed rebuilds roll preset changes back. Asset and mesh identity is retained through mesh combination, rebuilds, and dynamic LOD updates.

Use handle.getSourceShaderPreset() as the first layer when your overrides must preserve the source-authored preset. Keep wearable overrides under their asset ID rather than adding a wearable's global preset as an avatar-wide layer:

const shaders = getShaderManager();
shaders.setActiveAvatar(avatar.avatarId);
shaders.applyShaderPresetLayers([
avatar.getSourceShaderPreset(),
{ multiAsset: { [wearableId]: wearablePreset } },
].filter(Boolean));

material.userData.sourceAssetIds and sourceMeshIds expose source identity for inspection. Asset matching does not depend on display names or unique material names, and authored mesh targets inside an asset scope are normalized internally — so you don't need combined-mesh naming conventions.

Extra native textures are exposed as material.nafTextures; binding metadata is available under material.userData.nafTextureSlots.

RGBK colors

An RGBK mask is a texture that divides a material into up to four independently recolorable zones — one per channel: Red, Green, Blue, and K (the fourth/key channel). The mask doesn't hold the colors itself; it marks which pixels belong to which zone, so assigning a color to a channel recolors just that region. A single jacket texture can, for example, keep the body, trim, lining, and buttons on separate channels so each can be tinted independently without new textures.

Each zone maps to a color attribute ID — rgbk-r, rgbk-g, rgbk-b, and rgbk-k.

loadAvatar() and loadAvatarProgressive() apply valid source-authored RGBK swatches from wearables already included in a CAMP avatar, and equipping or replacing a CAMP wearable applies its swatches too. Colors use native attributes scoped to that asset; missing or invalid swatches leave existing values unchanged.

Use getColorAttributes() to discover which IDs an avatar actually exposes, then setColor(colorId, color, { assetId }) / unsetColor(colorId, { assetId }) for runtime edits.

note

Procedural texture baking must be enabled for these native color edits to affect baked output textures. RGBK colors are separate from Mega Stylizer presets and the shader manager's mask-color controls — getSourceShaderPreset() does not return RGBK color overrides.


Assets and cache

APIPurpose
preloadAssets(source, lods?)Resolve and cache all assets for an avatar source.
prefetchAsset(id, { version?, lod? })Cache one supported asset without decoding it.
prefetchAssets(requests)Fail-soft batch prefetch with per-item results and one trailing filesystem sync.

prefetchAssets() returns { results, succeeded, failed, synced }. Each result contains { key, id, version, lod, ok, error? }.


Voice helpers

Audio playback remains consumer-owned. The SDK parses response payloads and installs blendshape frames on an animation controller.

APIPurpose
configureVoice(config) / getVoiceConfiguration()Set or read response fields, frame width, frame rate, and default gain.
parseVoiceBlendshapes(response)Parse flat, nested, or indexed blendshape frames.
parseVoiceTags(response)Parse sorted gesture and emotion tags.
extractVoiceAudio(response)Return the configured base64 audio field.
loadVoiceAnimation(controller, frames, gain?)Install parsed frames on a controller.
loadVoiceConfig(path)Fetch and apply voice configuration JSON.

Events

Use on, once, and off. Both on() and once() return an unsubscribe function. Event emission is SDK-owned.

const unsubscribe = naf.on('avatar:ready', ({ avatarId, initialLod }) => {
console.log(avatarId, initialLod);
});
EventKey payload fields
initializedinstance
native-errorcaller, message
avatar:silhouettemesh, controller?
avatar:readyavatarId, mesh, controller, previousMesh, initialLod
avatar:loadedavatarId, handle
avatar:lod-upgradeavatarId, targetLod, assetId?
avatar:textures-updatedavatarId, skinnedMesh
avatar:rebuild-startavatarId, mesh
avatar:rebuiltavatarId, mesh, controller
avatar:unloadedavatarId
avatar:behavior-loadedavatarId, controller
avatar:dynamics-changedavatarId?, controller, source, active, error, overrideError
avatar:channel-startavatarId, channel, guid, duration, isLooping
avatar:channel-endavatarId, channel, guid, completed
avatar:erroravatarId?, error, phase
textures-readyavatars

Event error phases

The phase field on avatar:error identifies where in the pipeline the error occurred:

PhaseFatal?avatarId present?Description
avatar-resolveResolving an avatar source (such as a CAMP ID) failed.
avatar-loadYesNo (avatarDefinition included instead)Avatar load failed — all asset resolver fallbacks exhausted. The promise rejects.
nativeNoNoNative-side error forwarded during load (e.g. a single resolver endpoint failing before fallback). Informational only.
silhouetteNoNoSilhouette mesh construction failed. Avatar load continues.
silhouette-animationNoNoFailed to animate the silhouette. Avatar load continues.
animationNoYesAnimation controller or idle clip setup failed after avatar loaded.
behaviorNoYesSmart avatar behavior activation failed.
lod-upgradeNoYesA background LOD texture upgrade failed. Avatar remains at previous LOD.
rebuildYesYesFull rebuild (equipAsset/unequipAsset) failed. The promise rejects and the definition is rolled back.
rebuild-animationNoYesAnimation controller failed during rebuild staging. Rebuild continues without animation.
rebuild-behaviorNoYesBehavior re-activation failed after rebuild.
note

Fatal means the calling promise rejects. Non-fatal errors are emitted for observability but the pipeline continues. The operation's returned promise remains the flow-control contract — events are for observation.


Logging and diagnostics

APIPurpose
createLogger(tag)Return a tagged logger with debug, info, warn, and error.
setLogLevel(level) / LogLevelSet global verbosity: DEBUG, INFO, WARN, ERROR, or NONE.
setLogMemoryBufferSize(n)Set capacity; 0 clears and disables capture.
setLogMemoryBufferEnabled(enabled)Pause or resume capture.
isLogMemoryBufferEnabled()Return capture state.
getLogMemoryBuffer() / getLogMemoryBufferSize()Return a snapshot or entry count.
clearLogMemoryBuffer()Empty the buffer.
dumpLogMemoryBuffer()Print and return the current snapshot.
setLogBufferFullCallback(callback)Receive the snapshot before the oldest entry is dropped; pass null to clear.

Entries have { timestamp, level, caller, message }.


Advanced integrations

advanced is a deliberate boundary for wrappers, tooling, and custom renderers. The high-level SDK flows already own these operations.

caution

Do not mix advanced APIs into a managed lifecycle without an explicit ownership design.

NamespaceAPIs
advanced.runtimeensureInitialized(), reset()
advanced.renderingcreateMultiMeshAvatar, updateMaterialTextures, MegaShaderMaterial, OutlineMaterial, applyMegaShaderToMesh
advanced.animationPoseSynchronizer
advanced.materialCompatibilityUnity metallic-smoothness conversion and roughness/smoothness controls
advanced.debugLog window, animation overlays, and material overlays
advanced.filesystemsync, writeFile, readFile, listFiles
advanced.lodregisterCallbacks, unregisterCallbacks
advanced.configurationResolver, pipeline, texture, coordinate-space, culling-UV guard, and material compatibility configuration
advanced.defaultsRead-only mesh, rotation, conversion, skeleton, and blendshape defaults
advanced.diagnosticsProfiler, onNativeError
advanced.ikRecipesgetSources(avatarId), getMerged(avatarId)

advanced.ikRecipes.getMerged() returns immutable builder composition output, while controller.getRigRecipeConfig() returns the controller's effective runtime recipe after local edits.


Content authoring tools

The APIs above are for running avatars. Content Tools is a separate, optional toolset for authoring them — inspecting, editing, decoding, and regenerating the NAF FlatBuffer assets themselves. Most apps never need it; reach for it when you're building content pipelines or editor tooling.

Authoring tools are separate package subpaths and are not included in the runtime bundle:

  • @geniesinc/genies-naf-webgl/content-tools — browser-safe.
  • @geniesinc/genies-naf-webgl/content-tools/node — host orchestration.
  • @geniesinc/genies-naf-content-tools — an optional development dependency containing flatc, nafshc, and Content Schemas.
npm install --save-dev @geniesinc/genies-naf-content-tools

Browser-safe tools

AreaAPIs
Host transportNafContentToolsClient
Definitions and assetsinspectAvatarDefinition, inspectCombinableAsset, inspectMaterialTextures
Shader dataupsertShaderProgram, upsertShaderFlavor, getEmbeddedShaderFlavorData, upsertShaderSourceFlavor
RGBKupsertRgbkMask, getRgbkMask, configureRgbkMaterial
JSON extensionsinspectJsonExtension, getJsonExtension, upsertJsonExtension, removeExtension
Typed extensionsgetDynamicsConfig, upsertDynamicsConfig, getRigRecipeConfig, upsertRigRecipeConfig
RevisionsretargetCombinableAssetRevision, createLocalFixtureManifest, createLocalAvatarDefinition
Local fixturesclient.saveLocalFixtureAsset(...) persists one generated revision through the configured host.

Browsers cannot execute flatc or nafshc, so browser consumers must connect NafContentToolsClient to a trusted Node host.

Node tools

APIPurpose
NafContentToolsHostResolve and invoke native authoring tools.
NafContentToolsBridgeImplement the browser bridge contract.
createContentToolsBridgeHandler()Create a framework-neutral async request handler.
resolveContentToolsHost()Inspect resolved executable and schema paths.
Decode/encode helpersdecodeCombinableAsset, encodeCombinableAsset, decodeContentManifest, decodeEmbeddedCombinableAssetConfig, createCombinableAssetRevision
ShaderCompiler.compile(...) / compileSource(...)Produce slang-ir, glsl-es, and reflection metadata through nafshc.

Applications own bridge authentication, request limits, HTTP status mapping, and filesystem policy.