{"record":{"id":"e9fc43a5a71baec0","repo":"BabylonJS/Babylon.js","slug":"extensioncontext-direction-or-distance-propert","errorCode":null,"errorMessage":"${extensionContext}: Direction or Distance properties are not allowed on emitters attached to a scene","messagePattern":"(.+?): Direction or Distance properties are not allowed on emitters attached to a scene","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.pure.ts","lineNumber":118,"sourceCode":"     */\r\n    // eslint-disable-next-line no-restricted-syntax\r\n    public loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>> {\r\n        return GLTFLoader.LoadExtensionAsync<IMSFTAudioEmitter_EmittersReference>(context, scene, this.name, async (extensionContext, extension) => {\r\n            const promises = new Array<Promise<any>>();\r\n\r\n            promises.push(this._loader.loadSceneAsync(context, scene));\r\n\r\n            for (const emitterIndex of extension.emitters) {\r\n                const emitter = ArrayItem.Get(`${extensionContext}/emitters`, this._emitters, emitterIndex);\r\n                if (\r\n                    emitter.refDistance != undefined ||\r\n                    emitter.maxDistance != undefined ||\r\n                    emitter.rolloffFactor != undefined ||\r\n                    emitter.distanceModel != undefined ||\r\n                    emitter.innerAngle != undefined ||\r\n                    emitter.outerAngle != undefined\r\n                ) {\r\n                    throw new Error(`${extensionContext}: Direction or Distance properties are not allowed on emitters attached to a scene`);\r\n                }\r\n\r\n                promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter));\r\n            }\r\n\r\n            await Promise.all(promises);\r\n        });\r\n    }\r\n\r\n    /**\r\n     * @internal\r\n     */\r\n    // eslint-disable-next-line no-restricted-syntax\r\n    public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {\r\n        return GLTFLoader.LoadExtensionAsync<IMSFTAudioEmitter_EmittersReference, TransformNode>(context, node, this.name, async (extensionContext, extension) => {\r\n            const promises = new Array<Promise<any>>();\r\n\r\n            const babylonMesh = await this._loader.loadNodeAsync(extensionContext, node, (babylonMesh) => {\r","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.pure.ts#L100-L136","documentation":"In MSFT_audio_emitter, emitters attached to a scene node may not use spatial parameters (source, outerAngle/innerAngle, rolloffFactor, distanceModel, maxDistance, direction or distance gain parameters) because scene-attached emitters have no positional context. When loadSceneAsync finds any of these properties set on a scene-level emitter, it throws to prevent invalid audio configuration.","triggerScenarios":"Loading a glTF whose top-level extensions.MSFT_audio_emitter.emitters[] entries declare direction/distance/spatial fields (e.g. rolloffFactor, innerCone/outerCone angles, distanceModel, maxDistance, or gain with distance/directional values).","commonSituations":"Assets authored by tools that apply the same emitter object to both nodes and the scene; hand-written glTF where background audio emitters accidentally copy positional emitter settings.","solutions":["Remove direction/distance/angle/rolloff/distanceModel properties from scene-level emitters in the glTF.","Attach the emitter to a node instead if spatial audio behavior is intended.","Keep only gain and playback properties on scene-attached (background) emitters."],"exampleFix":"// before (scene emitter)\n{ \"index\": 0, \"innerAngle\": 30, \"outerAngle\": 60 }\n// after\n{ \"index\": 0 }","handlingStrategy":"validation","validationCode":"const spatial = ['direction','distance','innerAngle','outerAngle','rolloffFactor','distanceModel','maxDistance'];\nconst bad = sceneEmitter && Object.keys(sceneEmitter).some(k => spatial.includes(k));\nif (bad) throw new Error('Scene-level audio emitter must not declare spatial properties');","typeGuard":"function isNonSpatialEmitter(e: any): boolean {\n  return ['source','innerAngle','outerAngle','rolloffFactor','distanceModel','maxDistance','direction'].every(k => e?.[k] === undefined);\n}","tryCatchPattern":"try { await loader.loadAsync(url); } catch (e) {\n  if (e.message.includes('Direction or Distance properties')) {\n    // fix asset: strip spatial props from scene emitters, then retry\n  } else throw e;\n}","preventionTips":["Keep spatial properties only on node-attached emitters.","Add asset validation that separates background (scene) emitters from positional ones.","Avoid sharing emitter objects between scene and node contexts in authoring tools."],"tags":["gltf","audio","validation","extensions"],"backgroundTag":"invalid-emitter-configuration","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}