scena

Beautiful 3D in Rust, in the browser or native.

Real glTF, real PBR, named presets instead of magic numbers.

use scena::{Assets, EnvironmentPreset, Scene};

let assets = Assets::new();
let model = assets.load_scene("machine.glb").await?;
let env = assets
    .load_environment_preset(EnvironmentPreset::Studio)
    .await?;

let mut scene = Scene::new();
scene.instantiate(&model)?;
scene.add_studio_lighting()?;

renderer.set_environment(env);
renderer.render(&scene)?;
loading
Rendered live in your browser by scena.

Twelve materials. Twelve names.

Named PBR presets that produce credible materials under your chosen environment.

Selected: Chrome
select a material
let material = MaterialDesc::chrome();

Rendered live in your browser. Matte through glass use MaterialDesc::*; satin, leather, and rubber use source-backed Assets::material_presets(). Full physical glass remains capability-gated until its release proof is complete.

Drop a model. See it lit.

glTF or GLB. Materials, textures, and animation clips flow through the asset pipeline.

Drag a .glb or .gltf here
loading
let asset = assets.load_scene("watch.glb").await?;
let mut scene = Scene::new();
let import = scene.instantiate(&asset)?;
let bounds = import.bounds_world(&scene).ok_or("no bounds")?;
scene.add_studio_lighting()?;
scene.add_grid_floor(&assets, GridFloorOptions::new().under_bounds(bounds))?;
<scena-viewer src="watch.glb"
              environment="studio"
              camera-controls
              auto-rotate>
</scena-viewer>

Use Rust when you own the app. Use the custom element when you want a drop-in browser viewer.

Assemblies that mate themselves.

Authored connectors in your glTF. scena does the math.

shaft
hub
loading
let drive = scene.instantiate(&drive_part)?;
let load  = scene.instantiate(&load_part)?;

scene.mate(&drive, "shaft", &load, "hub")?;

No coordinates. No transform math. Two named connectors and one call.

Pick a name, not a number.

Most renderers make you write radians, lumens, EV. scena ships names. The numbers are still there when you need them.

Camera PerspectiveCamera::portrait()
Lighting DirectionalLight::key_light()
Environment EnvironmentPreset::Studio
Background Background::DarkStudio
Exposure AutoExposureConfig::product_studio()
Materials MaterialDesc::chrome()

Twelve material presets. Four camera lenses. Named lights, backgrounds, exposure scenarios, and environments. Every name in docs.rs/scena.

Production-ready, not just pretty.

Three backends.

Native via wgpu. WebGL2 and WebGPU in the browser. One library, one API.

Renderer::from_surface_async(surface).await?
Headless rendering.

Render PNGs without a window, on CI, in tests, and in generated docs.

headless_gltf_viewer("model.glb").render_png_bytes().await?
<scena-viewer>.

Three lines of HTML with model-viewer-style browser ergonomics.

<scena-viewer src="model.glb" environment="studio" camera-controls>
Doctor + diagnostics.

Capability reporting per backend and source-enforced contracts in CI.

cargo run -p xtask -- doctor --full

Typed scene-graph handles, explicit prepare / render lifecycle, capability matrix per backend, reference-image regression API, and a doctor that pins API contracts in CI.

Add it to your project.

Native, headless, WebGL2, WebGPU. One library.