Ultra-fast GPU-accelerated ambient occlusion lightmap baker that runs entirely in the browser. Loads GLB or FBX files, unwraps UV2 automatically via XAtlas, bakes AO via WebGL2 BVH ray tracing, and exports the result in multiple formats. Also available as a Chrome Extension that bakes directly inside BabylonJS Sandbox and Three.js Editor.
- Node.js 18+
- A browser with WebGL2 support (Chrome / Edge / Firefox)
npm install
npm run devThis starts two processes concurrently:
- Vite dev server — the browser app
- FBX converter server — local Express server on port
3001that handles FBX → GLB conversion viafbx2gltf
FBX support requires the converter server to be running. GLB files work without it.
You can run and package the application as a standalone desktop app using Electron. The app automatically starts the backend server in the background and loads the interface in a dedicated, high-performance window with full WebGL 2.0 and SharedArrayBuffer support.
To test the application in a desktop window:
npm run desktopTo compile the application into a standalone executable (.exe on Windows, .app/.dmg on macOS):
npm run packImportant
Windows Privilege Requirement (Symbolic Links Error)
On Windows, the packaging process (npm run pack) downloads code-signing utilities which contain macOS symbolic links (.dylib). Extracting these links requires permissions to create symbolic links on Windows.
If you get a Cannot create symbolic link: El cliente no dispone de un privilegio requerido error, resolve it in one of these ways:
- Run as Administrator: Close your terminal, open PowerShell/CMD as Administrator, navigate to the project directory, and run
npm run pack. Once the cache is extracted, you can compile as a regular user in the future. - Enable Developer Mode (Recommended): Go to Windows Settings > Privacy & security > For developers and turn Developer Mode ON. This permits symbolic links to be created without elevation.
The browser extension version allows you to run AO Lightmapper directly on 3D canvases inside BabylonJS Sandbox and Three.js Editor — no local server or file upload needed.
Note
Extension in Development: This extension is currently in active development and will be available soon on the Chrome Web Store.
| Site | URL |
|---|---|
| BabylonJS Sandbox | https://sandbox.babylonjs.com |
| Three.js Editor | https://threejs.org/editor |
![]() |
![]() |
![]() |
![]() |
👉 Watch the Demo Video (Direct Link)
Drag and drop a .glb or .fbx file onto the viewport, or click Open in the top-left of the sidebar to use the file picker.
Supported formats: GLB, FBX (FBX is automatically converted to GLB via the local server).
All settings are saved automatically to localStorage and persist between sessions. Click Reset to restore defaults.
| Setting | Options | Default | Description |
|---|---|---|---|
| Texture Size | 512 / 1024 / 2048 / 4096 | 1024 | Resolution of the baked lightmap texture |
| Format | PNG / JPG | PNG | Output image format. JPG produces smaller files; PNG is lossless |
| AO Samples | 16 – 1024 | 128 | Ray samples per texel. Higher = less noise, slower bake |
| AO Radius | 0.01+ | 1.0 | Radius of AO rays as a fraction of model size. Higher = softer, wider occlusion |
Denoiser — GPU bilateral filter applied after baking. Reduces noise while preserving edges using the G-Buffer world normals as a guide.
| Parameter | Range | Default | Description |
|---|---|---|---|
| Radius | 1 – 8 px | 4 px | Filter kernel size. Larger = smoother result |
| Passes | 1 – 3× | 1× | Number of denoising passes |
Seam Fix — UV dilation pass that fills empty texels at UV island borders, preventing visible seam lines on the 3D mesh.
| Parameter | Range | Default | Description |
|---|---|---|---|
| Strength | 1 – 5× | 3× | Number of dilation passes. Use 4–5 for meshes with large UV islands |
Click Generate Lightmap. The pipeline runs in four steps:
- UV Unwrap — XAtlas generates a non-overlapping UV2 atlas. Existing UV1 (texture coordinates) is preserved. The model is reloaded into the viewport with UV2 applied.
- GBuffer Pass — The mesh is rendered in UV2 space to produce world position and world normal per lightmap texel.
- AO Bake — A WebGL2 fragment shader casts cosine-weighted hemisphere rays against a BVH built from the merged scene geometry. Runs
Npasses with different random seeds and accumulates results. Progress is displayed live. - Post-Process — Denoising (bilateral filter) and dilation (seam fill) are applied if enabled.
A progress overlay with percentage is shown during baking. The lightmap preview panel appears in the bottom-right corner of the viewport when done.
After baking, the lightmap is applied to all mesh materials in the viewport. The AO Lightmap preview panel (bottom-right) provides:
- ON / OFF toggle — instantly enable or disable the lightmap in the viewport without rebaking. Useful for A/B comparison with the original material.
- Export button — downloads the full-resolution lightmap image in the configured format.
Two export formats are available:
Downloads a ZIP containing:
modelname.glb— the unwrapped model with UV2modelname_lightmap.png(or.jpg) — the lightmap as a separate image file
Use this when you need the lightmap as an external texture, for example to assign it to the ambient/occlusion slot in another tool.
Downloads a single .glb file with the lightmap embedded as the occlusion texture (occlusionTexture) on all materials, assigned to the UV2 channel (TEXCOORD_1). The file is self-contained and ready to use in any glTF-compatible renderer.
The lightmap is vertically flipped during embedding to match the glTF texture coordinate convention.
Batch mode bakes multiple files sequentially using the current sidebar settings.
- Click Open → select the Batch tab.
- Drag and drop files or click the drop zone to add GLB/FBX files to the queue.
- Duplicate files (same name + size) are ignored automatically.
Choose how the batch results are exported before running:
| Mode | Output | Description |
|---|---|---|
| Separate | batch_lightmaps.zip |
Each model exports as name.glb + name_lightmap.png — lightmap as a separate file |
| Embedded | batch_lightmapped_embedded.zip |
Each model exports as name_lightmapped.glb — lightmap embedded in the GLB as the occlusion/ambient texture on UV2 |
Click Bake All. Each file is processed in order:
- FBX → GLB conversion (if needed)
- UV2 unwrap via XAtlas
- Load into viewport
- AO bake
- Pack into ZIP
Status icons update per file: ○ pending → ◌ baking → ✓ done → ✕ error.
After completion the last successfully baked model is loaded into the viewport. Failed files are shown in the queue; details are in the browser console.
Click any mesh in the viewport to open the Material Inspector. A blue highlight indicates the selected mesh.
The inspector shows the material type badge (PBR or STD) and exposes:
| Field | Description |
|---|---|
| Albedo | Base color |
| Metallic | Metallic factor (0–1) |
| Roughness | Roughness factor (0–1) |
| Emissive | Emissive color |
| Field | Description |
|---|---|
| Diffuse | Diffuse color |
| Specular | Specular color |
| Emissive | Emissive color |
Both PBR and Standard materials expose their texture slots. Each slot supports:
- Click thumbnail — opens a full-resolution lightbox preview
- ↑ (Upload) — load a new image file from disk
- ⊞ (UV Fit) — reset UV tiling and offset to 1:1 (removes tiling/panning)
- × (Clear) — remove the texture from the channel
The last row in every texture list is AO Map, which shows the baked lightmap assigned to that mesh. Clicking × removes the lightmap from that specific mesh only. If no meshes retain a lightmap, the preview panel hides and export is disabled.
All material edits support Ctrl+Z undo (up to 64 steps).
Located in the View Settings section of the sidebar:
| Toggle | Description |
|---|---|
| Show UV Seams | Renders UV2 seam edges as neon pink lines in the viewport |
| Wireframe | Toggles wireframe overlay on all meshes |
| Show Normals | Renders per-vertex normal vectors as blue lines |
| Repair Mesh Normals | Analyzes mesh winding order per connected component and flips any inward-facing components. Recomputes smooth normals. Useful for FBX files with broken normals. |
Uses XAtlas (via WASM) to generate a non-overlapping UV2 atlas:
- Existing UV1 (TEXCOORD_0), positions, and normals are preserved and re-indexed to match the new vertex layout
- TANGENT attributes are removed (incompatible with atlas reindexing)
- Any existing TEXCOORD_1 is overwritten
Pure WebGL2, no server required. Pipeline per bake:
- BVH construction — CPU builds a bounding volume hierarchy from the merged scene geometry. Packed into RGBA32F textures and uploaded to the GPU.
- GBuffer pass — Renders the mesh in UV2 space. Outputs world-space position and normal per lightmap texel into floating-point render targets.
- AO accumulation — M passes (M = samples / 4), each with a different random seed. Per-texel cosine-weighted hemisphere sampling, ray cast against the BVH using Möller-Trumbore intersection. Ray max distance scales automatically to 15% of model bounding-box diagonal × AO Radius. Self-intersection bias is clamped between 0.1 mm and 2 cm.
- Denoising — G-buffer guided bilateral filter. Ping-pongs between two FBOs.
- Dilation — Outward-fill passes to pad UV island borders.
- Readback —
gl.readPixelswith Y-axis flip to produce a standard-orientation canvas.
Local Express server on port 3001. Receives raw FBX bytes, writes to a temp file, runs fbx2gltf, and returns the GLB. Also detects and removes the RootNode transform artifact produced by some FBX exporters (propagates scale/rotation to children before removing the root node).
- Separate ZIP — packs the unwrapped GLB + lightmap image using JSZip with DEFLATE compression.
- Embedded GLB — uses
@gltf-transformto insert the lightmap PNG asocclusionTextureon all materials withtexCoord: 1(UV2). The image is vertically flipped before embedding to match glTF conventions.
| Shortcut | Action |
|---|---|
Ctrl+Z |
Undo last material edit |
Escape |
Close material inspector / file modal / texture lightbox |
| Input | Action |
|---|---|
| Left drag | Orbit camera |
| Right drag / Middle drag | Pan |
| Scroll wheel | Zoom |
| Click mesh | Open Material Inspector |
| Click empty space | Deselect mesh |
| Library | Version | Role |
|---|---|---|
| @babylonjs/core | ^9.10.1 | 3D engine — scene, camera, lights, mesh picking, PBR/Standard materials, texture management |
| @babylonjs/loaders | ^9.10.1 | glTF/GLB import via SceneLoader |
| @babylonjs/serializers | ^9.10.1 | GLB serialization support |
| @gltf-transform/core | ^4.0.0 | Read/write GLB binary, traverse and mutate glTF documents |
| @gltf-transform/extensions | ^4.0.0 | glTF extension support (KHR_materials_*, EXT_meshopt_compression, etc.) |
| @gltf-transform/functions | ^4.0.0 | glTF transform utilities |
| jszip | ^3.10.1 | ZIP archive creation for batch and single exports |
| fbx2gltf | ^0.9.7-p1 | Native FBX → GLB conversion (runs server-side via Node) |
| express | ^5.2.1 | Local HTTP server that exposes the FBX conversion endpoint |
| cors | ^2.8.6 | CORS headers for the local converter server |
| concurrently | ^10.0.0 | Run Vite dev server and Node converter server in parallel with npm run dev |
| Library | Version | Role |
|---|---|---|
| vite | ^5.0.0 | Dev server and production bundler |
| playwright | ^1.60.0 | End-to-end testing |
| Library | Role |
|---|---|
| XAtlas | UV atlas generation — packs all mesh primitives into a non-overlapping UV2 lightmap atlas. Runs as a WASM module in the browser. |






