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.
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
| Goal | API |
|---|---|
| Load an avatar at one LOD | loadAvatar(source, options) |
| Load silhouette, progressive LODs, animation, and behavior | loadAvatarProgressive(source, options) |
| Load a wearable without an avatar | loadWearable(id, options) |
| Warm the complete avatar cache | preloadAssets(source, lods?) |
| Warm individual assets | prefetchAsset(...) / prefetchAssets(...) |
| Build a custom rendering integration | advanced.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 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>.
| Option | Purpose |
|---|---|
canvas | Existing canvas. The SDK creates one when omitted. |
width, height | Size of an SDK-created canvas. |
initScene(canvas, gl) | Return { renderer, scene, camera?, render? } to register the Web render host. |
environment | Asset environment: 'dev' or 'prod'. |
bearerToken | Global native REST bearer token. Empty enables native authentication fallback. |
services | Per-service { baseUrl?, bearerToken?, sdkVersion? } overrides. |
assetResolverConfigPath | Custom resolver configuration URL. |
textureSettingsPath | Custom texture settings URL. |
maxSupportedPipelineVersion | Highest supported asset pipeline version. |
managedRenderFrontend | Defaults to true. Set false only for a consumer-owned renderer. |
| WebGL context options | alpha, antialias, depth, stencil, preserveDrawingBuffer, powerPreference, premultipliedAlpha, desynchronized. |
Runtime APIs
| API | Purpose |
|---|---|
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_CONFIG | Read { buildType, isDebug, isRelease }. |
NafInstance | Convenience facade returned by initialize(). Named exports remain canonical. |
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.
| API | Purpose |
|---|---|
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('');
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
| API | Result |
|---|---|
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:
| Option | Purpose |
|---|---|
renderer | Required THREE.WebGLRenderer. |
lod | One LOD for loadAvatar; an order such as [2, 0] for progressive loading. |
meshOptions | Mesh construction options. |
proceduralTextures | Procedural textures are generated on the GPU as the avatar loads. Defaults to true; disable to use supported authored fallbacks. |
dynamics | Embedded dynamics by default, an override, or false to disable. |
signal | Cancels stale loads and suppresses late publication. |
Progressive-only options:
| Option | Purpose |
|---|---|
scene | Lets the SDK add/remove silhouette and final meshes without a visual gap. |
position, rotation | Initial transform shared by silhouette and final avatar. |
idle | The 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. |
behavior | Smart-avatar behavior — the configuration that decides which idle, gesture, and talking clips play and when. Requires animation. See the field table below. |
syncOptions | Skeleton, blendshape, and coordinate-conversion synchronization options. |
onSilhouette, onReady, onLodUpgrade, onError | Per-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. |
Native cache resolution may skip requested lower-quality LODs. The avatar:ready event reports the actual starting LOD as initialLod.
The behavior object:
| Field | Purpose |
|---|---|
config | A behavior configuration you already have in hand, as an object or JSON string. Using it skips the network fetch. |
behaviorId | A CAMP behavior-config ID (bm_behavior-config_*) for the SDK to fetch. |
slug | The legacy (pre-CAMP) way to identify a behavior for fetching. |
talking | Animation asset ID used for the talking motion. |
breathing | Animation asset ID used for the breathing motion. |
emotion | Default emotion clip, used when the behavior's demeanor doesn't specify one. |
onLoaded | Fires 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.
| Member | Purpose |
|---|---|
avatarId, mesh, controller | Runtime identity, Three.js group, and optional animation controller. |
status, isReady, isUnloaded | Lifecycle state. Do not drive a handle before isReady. |
currentLod | LOD currently rendered, or null. |
proceduralTexturesEnabled | Effective procedural texture mode. |
userData | A 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. |
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
| API | Purpose |
|---|---|
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. |
AvatarAnimationController | Public 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.
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
| Group | APIs |
|---|---|
| Lifecycle | update(dt), syncPose(), dispose(), hasAnimation, isPaused, isDisposed, blinkEnabled |
| Load clips | loadClip(source, version?), loadClipFromAssetResolver(...), loadClipFromBuffer(...) |
| Playback | setIdle, playGesture, setTalking, setEmotion, playEmotionBody, playGestureTag, playEmotion, playGUID |
| Time | getIdleTime, setIdleTime, getChannelInfo, getChannelCurrentTime, setChannelCurrentTime |
| Weights | setIdleWeight, setTalkingWeight, setTalkingEnabled, setEmotionWeight, setBoredomWeight, setGestureWeight, setBreathWeight, setAutoBoredom |
| Weight read-back | getChannelWeights, getTalkingWeight, getLookWeight, getGazeWeight |
| Retargeting | setAllRetargetEnabled, getAllRetargetEnabled, setRetargetEnabled, getRetargetEnabled |
| Look and gaze | setLookWeight, setGazeWeight, getHeadBone, setLookAtCamera, setAttentionTemplate |
| Behavior | loadSmartAvatarBehavior, loadSmartAvatarBehaviorFromConfig, getBehaviorConfig, setBehaviorConfig, getAvailableAnimTags |
| Graph tooling | serializeGraph, applyGraphParams |
| Current state | getCurrentBoredGUID, getCurrentSecondaryGUID, getCurrentGestureGUID, getCurrentEmotion |
| Voice | setVoiceAnimation, 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.
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:
| Area | APIs |
|---|---|
| Dynamics | appendDynamicsGroup, clearDynamics, resetSimulation, setDynamicsEnabled, setDynamicsWeight, getDynamicsWeight, getLastDynamicsGroupError |
| Rig recipe | setRigRecipeConfig, getRigRecipeConfig, getLastRigRecipeConfigError |
| IK | setFBIKEnabled, getFBIKEnabled |
| Body motion config | setBodyMotionConfig, getBodyMotionConfig, getLastBodyMotionConfigError, getBodyMotionDiagnostics |
| Body motion commands | playBodyMotionGesture, 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.
| API | Purpose |
|---|---|
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. |
RendererFallbackPolicy | STRICT or DISABLE_UNSUPPORTED. |
RendererTextureSlot | BACKGROUND, 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.
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.
| API | Purpose |
|---|---|
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.outline | Typed 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:
cartoonMixblends between realistic and toon shading:0is fully lit (realistic),1is fully toon, and values in between are a hybrid.aoAlbedoDarkendarkens the base color in ambient-occlusion areas — crevices and contact shadows — for a more hand-painted look.0leaves the color untouched; higher values deepen it.useOrmAlpha(0–1) applies to glTF materials only; it's ignored (forced to0) 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.
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:
alphaMode—OPAQUE,MASK(a hard cutout usingalphaCutoff), orBLEND(soft transparency).alphaCutoff— the threshold used byMASK.opacity— overall opacity. Settingopacityon its own does not turn on transparency; usealphaMode: "BLEND"for that.doubleSided— render both faces of a surface instead of culling the back.depthWrite— whether the material writes to the depth buffer.BLENDdefaults this tofalse.
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:
alphaOpacity→opacitytwoSided→doubleSidedalphaEnabled→alphaMode:falsebecomesOPAQUE;truebecomesMASKwhen a positivealphaCutoffis set, otherwiseBLEND.
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():
- Layers are applied from lowest to highest priority.
- Later values replace earlier values, including arrays and non-scoped objects.
multiMesh,multiMaterial, andmultiAssetmerge recursively by target/property.nullremoves an inherited property, target, or map;undefinedis ignored.- 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.
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
| API | Purpose |
|---|---|
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.
| API | Purpose |
|---|---|
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);
});
| Event | Key payload fields |
|---|---|
initialized | instance |
native-error | caller, message |
avatar:silhouette | mesh, controller? |
avatar:ready | avatarId, mesh, controller, previousMesh, initialLod |
avatar:loaded | avatarId, handle |
avatar:lod-upgrade | avatarId, targetLod, assetId? |
avatar:textures-updated | avatarId, skinnedMesh |
avatar:rebuild-start | avatarId, mesh |
avatar:rebuilt | avatarId, mesh, controller |
avatar:unloaded | avatarId |
avatar:behavior-loaded | avatarId, controller |
avatar:dynamics-changed | avatarId?, controller, source, active, error, overrideError |
avatar:channel-start | avatarId, channel, guid, duration, isLooping |
avatar:channel-end | avatarId, channel, guid, completed |
avatar:error | avatarId?, error, phase |
textures-ready | avatars |
Event error phases
The phase field on avatar:error identifies where in the pipeline the error occurred:
| Phase | Fatal? | avatarId present? | Description |
|---|---|---|---|
avatar-resolve | — | — | Resolving an avatar source (such as a CAMP ID) failed. |
avatar-load | Yes | No (avatarDefinition included instead) | Avatar load failed — all asset resolver fallbacks exhausted. The promise rejects. |
native | No | No | Native-side error forwarded during load (e.g. a single resolver endpoint failing before fallback). Informational only. |
silhouette | No | No | Silhouette mesh construction failed. Avatar load continues. |
silhouette-animation | No | No | Failed to animate the silhouette. Avatar load continues. |
animation | No | Yes | Animation controller or idle clip setup failed after avatar loaded. |
behavior | No | Yes | Smart avatar behavior activation failed. |
lod-upgrade | No | Yes | A background LOD texture upgrade failed. Avatar remains at previous LOD. |
rebuild | Yes | Yes | Full rebuild (equipAsset/unequipAsset) failed. The promise rejects and the definition is rolled back. |
rebuild-animation | No | Yes | Animation controller failed during rebuild staging. Rebuild continues without animation. |
rebuild-behavior | No | Yes | Behavior re-activation failed after rebuild. |
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
| API | Purpose |
|---|---|
createLogger(tag) | Return a tagged logger with debug, info, warn, and error. |
setLogLevel(level) / LogLevel | Set 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.
Do not mix advanced APIs into a managed lifecycle without an explicit ownership design.
| Namespace | APIs |
|---|---|
advanced.runtime | ensureInitialized(), reset() |
advanced.rendering | createMultiMeshAvatar, updateMaterialTextures, MegaShaderMaterial, OutlineMaterial, applyMegaShaderToMesh |
advanced.animation | PoseSynchronizer |
advanced.materialCompatibility | Unity metallic-smoothness conversion and roughness/smoothness controls |
advanced.debug | Log window, animation overlays, and material overlays |
advanced.filesystem | sync, writeFile, readFile, listFiles |
advanced.lod | registerCallbacks, unregisterCallbacks |
advanced.configuration | Resolver, pipeline, texture, coordinate-space, culling-UV guard, and material compatibility configuration |
advanced.defaults | Read-only mesh, rotation, conversion, skeleton, and blendshape defaults |
advanced.diagnostics | Profiler, onNativeError |
advanced.ikRecipes | getSources(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 containingflatc,nafshc, and Content Schemas.
npm install --save-dev @geniesinc/genies-naf-content-tools
Browser-safe tools
| Area | APIs |
|---|---|
| Host transport | NafContentToolsClient |
| Definitions and assets | inspectAvatarDefinition, inspectCombinableAsset, inspectMaterialTextures |
| Shader data | upsertShaderProgram, upsertShaderFlavor, getEmbeddedShaderFlavorData, upsertShaderSourceFlavor |
| RGBK | upsertRgbkMask, getRgbkMask, configureRgbkMaterial |
| JSON extensions | inspectJsonExtension, getJsonExtension, upsertJsonExtension, removeExtension |
| Typed extensions | getDynamicsConfig, upsertDynamicsConfig, getRigRecipeConfig, upsertRigRecipeConfig |
| Revisions | retargetCombinableAssetRevision, createLocalFixtureManifest, createLocalAvatarDefinition |
| Local fixtures | client.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
| API | Purpose |
|---|---|
NafContentToolsHost | Resolve and invoke native authoring tools. |
NafContentToolsBridge | Implement the browser bridge contract. |
createContentToolsBridgeHandler() | Create a framework-neutral async request handler. |
resolveContentToolsHost() | Inspect resolved executable and schema paths. |
| Decode/encode helpers | decodeCombinableAsset, 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.