
These are the main real-time 3D submission and render-control surfaces.
Primary 3D drawing/control methods:
drawMesh(meshHandle, materialHandle, transformOrNode)billboard(textureHandleOrSource, options)drawSkybox(textureHandleOrSpec)setEnvironmentMap(pathOrCubeCameraOrNull)clear3d(color)setFog(fogSpec)setAtmosphereProfile(profileOrNull, overrides?)clearFog()setOcclusionCulling(enabledOrOptions)addDecal(options)createCubeCamera(options?)updateCubeCamera(cubeCameraHandle)destroyCubeCamera(cubeCameraHandle)createRenderTarget(width, height, options?)setRenderTarget(renderTargetHandleOrNull, captureCamera?)destroyRenderTarget(renderTargetHandle)createLine(options)createLineSegments(options)updateLine(lineHandle, patch)destroyLine(lineHandle)clearStencil(value?)PostFX lives here today, not in a separate namespace:
setPostFXPass(name, options)setPostFXEnabled(name, enabled)removePostFXPass(name)getPostFXState()registerPostFXShader(name, wgslSource)Stage 180 landed stock named postFX presets on top of this same seam rather than creating a separate post-processing namespace.
The retained fog seam still starts at setFog(...), and Atmosphere V1 still
layers onto that same entry point instead of introducing a second renderer
namespace.
Stage 195 adds one thin authored helper on top of that retained floor:
setAtmosphereProfile(profileOrNull, overrides?)Built-in profiles today:
neon-nightstorm-hazeThe helper is intentionally narrow. It only composes:
setSkyGradient(...)setFog({ ..., atmosphere })It does not own drawSkybox(...), setEnvironmentMap(...), local fog
volumes, or any deeper volumetric system.
Example:
aura.draw3d.setAtmosphereProfile('neon-night', {
density: 0.045,
});
Truthful boundaries:
If you are using the helper, prefer:
aura.draw3d.setAtmosphereProfile(null);
That keeps the profile-owned sky gradient and the retained fog/Atmosphere V1 state coherent.
clearFog() still remains the lower-level seam that clears the retained fog
state and the coupled Atmosphere V1 overlay only.
Stage 191 closes one bounded live cube-camera capture lane on top of the existing command surface.
Public seam:
createCubeCamera({ position?, near?, far?, resolution?, facesPerFrame? })updateCubeCamera(cubeCamera)destroyCubeCamera(cubeCamera)setEnvironmentMap(cubeCamera) to use the captured cubemap as the shared
live IBL sourceRuntime and cost truth:
updateCubeCamera(...) or position changes
mark all six faces dirty againfacesPerFrame clamps to 1..6 and defaults to 6facesPerFrame: 2 settles over
three frames instead of forcing all six faces in one frameaura.material.setCubeMapTexture(...)Current explicit boundaries:
aura.material.setCubeMapTexture(material, cubeCameraOrNull) now ships one
bounded per-material override over the same cube-camera source lane; null
clears back to the shared scene IBL sourceExample:
const cubeCam = aura.draw3d.createCubeCamera({
position: { x: 0, y: 2.5, z: 0 },
resolution: 256,
facesPerFrame: 2,
});
aura.draw3d.setEnvironmentMap(cubeCam);
// Optional per-material override over the same live cube-camera source.
aura.material.setCubeMapTexture(chromeMaterial, cubeCam);
// Refresh the shared live IBL source after moving the probe.
aura.draw3d.updateCubeCamera(cubeCam);
// Revert one material back to the shared scene IBL source.
aura.material.setCubeMapTexture(chromeMaterial, null);
Custom WGSL postFX now has one explicit truthful input contract:
@binding(0) input_texture: texture_2d<f32>
current scene color immediately before this custom pass@binding(1) input_sampler: sampler
shared filtering sampler for scene-color reads@binding(2) u_postfx
resolution, texel size, stock params, time, 8 custom param slots, camera
near/far, projection mode, and step/input-contract flags@binding(3) depth_sampler: sampler_comparison
comparison sampler for depth-aware reads@binding(4) depth_texture: texture_depth_2d
scene depth from the current swapchain 3D pass@binding(5) original_scene_texture: texture_2d<f32>
stable scene-color copy from before the postfx chain startedTruthful boundaries:
aura.draw3d.getPostFXState() now also reports:
resolvedSteps for the deterministic runtime execution ordercustomShaderBindings for the public binding mapcustomShaderContract for the supported input semantics and remaining
limitsStage 213 also ships one bounded retained postFX-graph owner on top of this same floor:
createPostFXGraph({ enabled?, passes, targetChain? })updatePostFXGraph(graph, patch?)destroyPostFXGraph(graph)getPostFXGraphState(graph?)Current truthful graph semantics:
1 and 4 pass
entries2 named intermediate targets through one shared
targetChainclear, not "use the previous graph"setPostFXPass(...), setPostFXEnabled(...), and removePostFXPass(...)
is outside the mixed-ownership lane and currently rejects with
postfx_invalid_optionsThis remains intentionally narrow:
aura.postfx namespaceStage 191 adds one stock global outline lane on top of the existing postFX composer seam:
aura.draw3d.setPostFXPass('outline', {
strength: 0.82,
radius: 1.5,
threshold: 0.35,
customParams: {
depthThreshold: 0.018,
lumaThreshold: 0.08,
outlineR: 0.08,
outlineG: 0.95,
outlineB: 1.0,
},
});
Truthful boundaries:
original_scene_texture copy to keep edge detection from drifting after
earlier postFXstrength, radius, threshold,
depthThreshold, lumaThreshold, and outlineR/G/Baura.particles3d.draw() currently renders through the same billboard/material
submission seam described below rather than through a separate GPU-simulated
particle pipeline.
Current truthful runtime behavior:
create/destroy/emit/burst/pause/resume/stop/getStateburst(options) emitters auto-clean after their particles emptydraw() submits billboard-backed particle draws through the shared Draw3D
billboard lane, so the accepted render paths are:setRenderTarget(handle, ...)
stays active for that frame, including the alternate capture-camera formaura.debug.inspectorStats() now includes scene3dRuntime.particles3d so
particle counts, draw calls, and lifecycle state do not stay opaqueCurrent postfx truth:
draw3d plus
particles3d seamexamples/neon-district,
where fx.js pairs the neon-night atmosphere profile with the authored
monitor capture propaura.draw3d.billboard(...) is the lightweight camera-facing quad path for
actors, effect cards, and simple in-world screens.
Truthful texture-source inputs today:
{ dataTextureHandle } bridge object for procedural billboards backed by
aura.material.createDataTexture(...)The bridge object exists because data textures and materials do not share one safe public handle namespace. Use the bridge form for procedural/no-asset billboards instead of guessing that a bare overlapping numeric handle will resolve the way you intended.
aura.particles3d.draw() currently uses aura.draw3d.billboard(...) on the
shared scene3d runtime path.
Focused truth after Stage 187:
emit, pause, resume, stop,
draw, and getStateaura.draw3d.addDecal(options) is currently a narrow detail-overlay seam.
The shipped renderer path submits oriented decal quads / decal plane batches
that you place explicitly in the world.
Use it for authored surface detail such as signage overlays, puddle-edge accents, grime cards, scorch marks, or graffiti planes.
Accepted authored albedo sources today:
texture: 'path/to/decal.png'textureHandle: aura.material.createDataTexture(...)color: { r, g, b, a? } for a flat-color fallback or tintYou can combine color with either texture source as a tint, but not with
both texture and textureHandle at the same time.
Current geometry truth:
size.x / size.z define the visible overlay footprintsize.y stays part of the authored payload for future projected-volume
compatibility, but it does not currently widen the live overlay mesh into a
true receiver-projected decal volumeIt does not currently guarantee true projected decals onto arbitrary scene geometry. In particular, it is not a mesh-conforming deferred decal pass that automatically wraps across mixed receiver topology.
aura.draw3d.createRenderTarget(...) plus
aura.draw3d.setRenderTarget(renderTargetHandleOrNull, captureCamera?) is the
explicit offscreen full-scene 3D capture lane.
Current truthful modes:
setRenderTarget(handle) redirects the current frame's full 3D scene pass
into that render target using the current camera statesetRenderTarget(handle, { position, target, up?, fovDegrees?, near?, far? })
redirects that full 3D scene pass into the target from an alternate
perspective camerasetRenderTarget(null) restores rendering to the swapchainCurrent explicit boundaries:
setRenderTarget(...) is a frame-latched redirect for the current 3D scene
pass, not a mid-frame per-command split graphparticles3d draws participate in this whole-frame capture
path because they submit on the same Draw3D billboard laneProjection, transform, and control helpers:
perspective(fov, near, far)orthographic(sizeOrBounds, near, far)setProjectionMode(mode)setOrthoSize(size)getProjectionMode()lookAt(x, y, z)setPosition(x, y, z)setTarget(x, y, z)setFOV(fov)getViewMatrix()getProjectionMatrix()setControlProfile(profile, options)updateControls(dt, inputState)getControlState()Use cases:
Light creation and updates:
ambient(color, intensity?)hemisphere(skyColor, groundColor, intensity?, upDirection?)directional(direction, color, intensity?)point(position, color, intensity?, range?)spot(position, direction, color, intensity?, range?, angle?)update(lightHandle, patch)remove(lightHandle)Shadow control also lives here today:
configureDirectionalShadows(options)configureShadow(lightHandle, options)getShadowState()getShadowStats()setShadowCasting(nodeOrHandle, enabled)setShadowQuality(levelOrOptions)setShadowBudget(options)Important note:
aura.shadow namespace today.aura.light.If you are planning "AAA next steps", do not treat the following as missing engine features in native AuraJS:
outline postFX pass now ships on the same seamStage 180 also closed the previously drifting seams:
The remaining frontier is broader renderer breadth, especially true projected decals and wider render-path coverage, not rediscovering the shipped quick wins above.
Stage 180 surfaced the shipped quick wins with copyable snippets and example coverage.
Quick-win starter snippet:
aura.draw3d.setEnvironmentMap('env/cyberpunk.hdr');
aura.draw3d.setOcclusionCulling({ enabled: true, conservative: true });
aura.light.spot(
{ x: 8, y: 6, z: 8 },
{ x: -0.45, y: -1.0, z: -0.25 },
{ r: 0.45, g: 0.78, b: 1.0 },
1.35,
28,
0.72,
);
Material-side follow-up lives on the next page:
aura.material.setEmissive(handle, color, strength?)aura.material.setClearcoat(handle, clearcoat, roughness)aura.mesh.createPlane(width, depth, widthSegments, depthSegments)aura.material.create({ sheenColor, sheenRoughness })aura.material.setTextureTransform(...)aura.material.createDataTexture(...)aura.material.setStencil(...)Current truthful boundary:
createPlane(...) API with extra
optional segment countsaura.postfx namespace today.aura.shadow namespace today.aura.scene3d handles hierarchy, imported-scene metadata, clip helpers, and render binding; it does not replace aura.draw3d.drawMesh(...) ownership.For scene graph, meshes, materials, glTF import, and skinned meshes, continue to: