01 Local-first IES simulation

Light, before
the room exists.

A local-first IES lighting simulator for the browser: import real photometry, model a room, aim your luminaires, or just describe the space — and read the work plane as a heatmap with engineering metrics. Direct light plus up to four diffuse bounces, calculated on your machine.

Free account No project uploads Installable offline PWA

LM-63 · Type C
TILT=NONE photometry
Direct + up to 4
Diffuse bounces
Local-first
IndexedDB · offline PWA
Plate 01 / Plan Atelier · 6.00 × 4.00 × 3.00 m · as new
LuxLab plan view: a 6 by 4 metre room with a false-colour work-plane heatmap, six downlights and a 105 to 741 lux legend.
Average
467 lx
Uniformity
23%
Target
79% ≥ 300 lx
Fig. 01 — Demo atelier: six 2,400 lm downlights, a 64 × 43 grid and 450 surface patches. Mean 467 lx as new. At the default 0.80 maintenance factor the same room reads 373 lx maintained.

01 What it does

A lighting study, not a render.

Photometry, geometry and result stay visible and editable. No hidden defaults, no black-box solver.

01 / Input

Import real photometry

Drop in an LM-63 Type C file. The parser reads the angle tables, symmetry and candela, draws a polar distribution, and surfaces the lumens, watts and maximum candela behind it.

LM-63 Type C TILT=NONE
02 / Model

Build the room, aim the light

Set a rectangular room, work-plane height and surface reflectances. Place luminaires by hand, or stamp a block of them with grid placement, then tune mounting height, aim, tilt and dimming in the inspector.

X/Y Mount Aim Tilt Dim
03 / Result

Read the work plane

Switch between total, direct and reflected illuminance. Track average, minimum, maximum and uniformity, check a target-lux threshold, and hover the grid for a point reading. Up to three labelled isolux contours trace the levels that matter, and a maintenance factor turns as-new figures into the maintained values you specify against.

Average Min Max U0 Target
04 / Ask

Describe it, then calculate it

Type the room and the light you want — "20 × 15 m office, 500 lx" — and LuxLab builds the scene, sizes the layout by simulation and reports the result. DeepSeek only reads the brief; geometry, photometry and every lux value stay in the engine. The prompt is opt-out in Settings.

Prompt DeepSeek parse Local solve
Try the Ask bar

02 How it works

From .ies to lux in three moves.

A short loop: edit, debounce, calculate. The worker keeps the interface responsive while the grid runs.

01 Parse

Import and validate

Open the IES library and load a Type C file. LuxLab checks the header and angle tables, expands symmetry, and builds a bilinear candela sampler.

Rejects TILT=INCLUDE Type A/B

02 Model

Place and aim

Set the room and work plane, then place luminaires in plan. Use the inspector for mounting height, aim, tilt and dimming. The 3D view is read-only.

Walls block light No shadows yet

03 Read

Calculate and inspect

Recompute and read the false-colour map. Compare total, direct and reflected views, then check the metrics and the target threshold.

Direct + up to four bounces

03 Local-first

The calculation never leaves the tab.

No project uploads and no server-side render. An account gates the tool; the geometry, photometry and results stay in the tab. If you use Ask LuxLab, the sentence and a list of fixture names go to DeepSeek; turning the assistant off in Settings sends nothing. Projects and IES source text live in IndexedDB, and the service worker keeps the tool available offline. Close the lid and the room is still there.

Private by default

No project uploads and no tracking cookies. Your project stays browser state, not a server record; page views are counted anonymously. The assistant is the one network feature, and it is opt-out.

Instant feedback

The engine runs in a Web Worker. Edits debounce for 360 ms; only the latest calculation survives.

Install it and unplug

An offline PWA. The app shell, fonts and the lazy 3D preview are precached after one visit.

Portable projects

Exports are self-contained .luxlab.json files with every referenced IES profile embedded.

IES Parser Worker Canvas IndexedDB
Plate 02 / 3D preview Projects stay in the browser
LuxLab 3D preview: a transparent room shell containing six luminaires above a false-colour work-plane heatmap.
6 luminaires 450 patches 467 lx mean as new
Fig. 02 — Work-plane illuminance in the read-only 3D preview. Same 450 patches; rendered locally.

04 The plan

Credible studies, at browser speed.

v0.10.0 is tagged and in use. Describe a space in the Ask bar and LuxLab builds, solves and calculates it; geometry is here too: rooms are polygons on a shared floor, walls block light, and a stepped outline is something you draw. Shadows and reporting are next, in the order the method can support honestly.

The goal

Make a credible IES illumination study as quick as opening a tab: fast enough to iterate, private enough for client work, and accessible enough that photometry isn't locked inside desktop software.

Shipped now

Shipped
  • One or more rooms on a shared floor plan, each with its own ceiling
  • Polygon room outlines, with walls that block light
  • Stepped and L-shaped rooms you can draw and reshape
  • Two workspaces: warm paper, or the OrbitX dark palette
  • A floor summary, area-weighted across every room
  • Type C, TILT=NONE IES import with polar preview
  • OrbitX catalogue: 97 IES profiles across 20 families
  • Direct illuminance plus up to four diffuse bounces
  • Multi-select on the plan, to move or delete a group at once
  • 2D plan editor and read-only 3D preview
  • Average, min, max, U0 and target compliance
  • Up to three labelled isolux contours
  • Maintenance factor: fixed, or the CIE 97 four-factor method
  • Grid placement for blocks of luminaires
  • Accounts with roles, an admin dashboard and first-run password change
  • IndexedDB projects with self-contained JSON export
  • Installable, fully offline PWA

Next

Unscheduled
  • Freeform outlines at any angle, and rooms that share a doorway
  • Openings, furniture and shadow rays
  • CSV and PDF report export
  • Glare metrics, including UGR
  • Type A/B photometry and TILT data
  • Multi-bounce or path-traced verification

No dates. Scope follows what the method can support honestly.

05 Method

A short, inspectable pipeline.

Parse, interpolate, integrate, display. Every result is reproducible from the inputs, in the browser.

  1. 01LM-63
  2. 02Bilinear
  3. 03Worker
  4. 04Typed arrays
  5. 05Canvas
  6. 06IndexedDB
01

LM-63 parser

Type C, TILT=NONE, header variants and symmetry expansion. Unsupported photometry is rejected, not approximated.

02

Bilinear candela

Vertical and horizontal interpolation with 360° wrap-around. Dimming and the maintenance factor scale candela linearly, per luminaire.

03

Web Worker

The pure engine runs off the main thread. Edits debounce for 360 ms and the latest request wins.

04

Deterministic heatmaps

The inferno scale is driven by the data's min and max. No random dithering, no per-frame noise.

05

Local IndexedDB

Projects and IES source text persist in the browser. Exports embed every referenced profile.

06

Offline PWA

The app shell, fonts and lazy 3D chunk are precached after the first visit. No CDN in the path.

Calculation

Direct E = I(γ, C) · cos ε / r² Up to four bounces Bᵢ¹ = ρᵢ · Eᵢ Bᵢk+1 = ρᵢ · Σⱼ Bⱼk Aⱼ cos θⱼ cos θᵢ / (π rᵢⱼ²) Eref(p) = Σₖ Σᵢ Bᵢk Aᵢ cos θᵢ cos θₚ / (π rᵢ²) Maintained Em = MF · (Ed + Eref), MF = LLMF · LSF · LMF · RSMF

Bilinear candela in γ and C. One bounce by default and up to four; still an estimate, not a full radiosity solve.

Fixture check

750 lm declared 748 lm integrated

A public Cooper Lighting MRZ Type C profile was integrated at 1°. The check exercises angle handling and flux integration — it is not a certification.

Third-party fixture Not a compliance claim

06 Changelog

What shipped, in order.

Generated from the project changelog on every deploy, so the page cannot fall behind the release.

v0.10.0

Added

  • Ask LuxLab: a sentence becomes a calculated study. A prompt bar sits under the header on the project list and in the workspace. Describe the space and the light — "20 × 15 m open-plan office, 500 lx" — and LuxLab builds the project, resolves a supported IES profile, places the luminaires, runs the engine and reports the result with every assumption. DeepSeek reads the brief; geometry, photometry and every lux value are calculated locally.
  • Fixture choice that names its substitutes. The brief is matched against the 95 supported OrbitX profiles and your own library. An exact catalogue or library ref is honoured; anything else resolves to a real profile, and the transcript names the one it used and why.
  • Counts solved by simulation. Say "500 lx" without a count: LuxLab estimates with the lumen method, then simulates a ladder of layouts to find the smallest count that meets the target, with a 3 % design margin. Say "12 downlights" and it places exactly twelve.
  • Follow-ups and undo. A second sentence refines the AI-created project in place; changing the room renames it, and one click restores the previous version. The transcript keeps the brief, assumptions, compliance and metrics for every turn.
  • An opt-out and a visible disclosure. The assistant is on by default and can be turned off in Settings. While it is on, the prompt and a list of fixture names go to DeepSeek; projects, photometry and results stay in the browser. The key lives only in the accounts service.

Changed

  • The accounts service gains /api/ai/interpret and /api/ai/status: session and CSRF protected, rate limited per account (six prompts per ten minutes, 40 a day by default) and capped at three concurrent calls by default.
  • The landing page's privacy copy now states plainly what the assistant sends and what it never sends.

v0.9.0

Added

  • Up to four diffuse bounces. The engine could solve one bounce of interreflection; it now iterates, so light that bounces from a wall onto the ceiling and back down is part of the answer rather than being dropped after the first hop. One to four, chosen per project, with the default left at one so every saved design keeps the numbers it was calculated with.
  • Select several luminaires and delete them together. Shift- or Cmd-click in the plan or the list builds a selection; the inspector still edits one fixture, and Delete (or the "Delete N" button) removes the whole set in a single update — one autosave, one recalculation. Select all scopes to the active room.
  • A 100 % maintenance preset. As-new is a legitimate comparison, so it is a button beside 70, 80 and 90 rather than only the far end of the slider.

Changed

  • Bounce count shares the patch budget: four bounces use about a quarter of the patches of one, and the solve stops early when a layer adds less than a thousandth of the previous one.
  • The results panel footnote states the number of bounces actually solved, and the landing's method card now shows the iterative form rather than a single pass.

v0.8.0

Added

  • A settings screen with a second theme. OrbitX turns the workspace into the industrial palette it is named after — deep blue-black surfaces, light text and a single electric-blue accent (#0084FF) — while Lab keeps the original warm-paper workspace. The choice is reachable from the gear in the header, and from the project list so it can be set before opening anything.
  • The theme reaches everything the eye lands on: chrome, panels, forms, buttons, modals, the plan sheet (a dark blueprint rather than paper), the polar plot in the IES library, the 3D room shell and the browser's own chrome colour. The heatmap keeps its inferno scale in both, because the scale encodes illuminance rather than style.

Changed

  • The palette is now entirely tokenised. Surfaces that were tinted with literal white are driven by a raised-surface token, so a theme can restate them without a component knowing which one is running.
  • Theme is stored per browser, not per project: a shared export keeps its geometry and numbers but not its colours, and switching machines does not carry a preference.

v0.7.0

Added

  • Stepped and L-shaped rooms you can draw. Every edge of the active room carries a + that cuts a rectangular step into it, every corner is a handle you can drag, and a step can be removed again from the room panel. A dragged corner takes each neighbour with it on that neighbour's own axis, so the outline stays rectilinear without the pointer having to be careful; an edit that would cross a wall or collapse an edge is refused rather than written, and the shape keeps its last good state.
  • The 3D view extrudes the real outline: a floor and ceiling from the polygon and one wall per edge, instead of a box drawn around it. The stepped room and its notch are visible from the inside.

Changed

  • Width and depth in the room panel become read-only once an outline is stepped, because they are then derived from a shape that a single pair of numbers cannot describe. The area, the room list and the plan's own labels carry the size instead.

v0.6.0

Added

  • More than one room in a project. Rooms sit on a shared floor plane, the plan draws the whole floor, and clicking a room makes it the one you are working on. Each room registers in the room panel with its area and fixture count, and can be added, duplicated, renamed or deleted — deleting a room deletes its luminaires, and a project always keeps at least one.
  • Rooms are polygons, not rectangles. The engine takes a footprint instead of a width and a depth, so a stepped or L-shaped outline is a shape the calculation understands rather than a bounding box it quietly fills in. Where the outline is not convex, walls now block light: a fixture in one arm no longer illuminates the other through a wall.
  • A floor summary. One calculation covers every room and returns a result for each, plus a total weighted by floor area — a 4 m² cupboard does not get the same vote as a 24 m² studio. The results panel lists each room's average and uniformity, totals them, and switches rooms on click.
  • Fixtures belong to a room now. Dragging one into another room hands it over, and fixtures in other rooms are drawn faded. Grid placement follows the outline, so a grid over an L places only where the room actually is.

Changed

  • The project format is version 2. A version 1 project or bundle opens unchanged: its single room becomes a four-vertex polygon and its luminaires adopt that room, so saved designs keep their numbers. An older app refuses a version 2 file with a clear message rather than drawing a rectangle around a floor plan.
  • A rectangular room can still be sized by width and depth in the panel, or by dragging a corner handle on the plan. Once an outline is stepped, width and depth become read-only.
  • luxlab-update installs and validates deploy/Caddyfile on every deploy instead of only reloading Caddy, so the installed config can no longer drift from the repository. A config that fails validation is never installed.

Fixed

  • A room with no light in it no longer paints a zero-lux heatmap over itself, which filled the room with the colormap's near-black end and read as a hole in the plan.
  • The method footnote no longer calls itself a convex-room approximation; walls block light, and the copy says what the method does.

07 Ready when you are

Start with a room.
Leave with a number.

One click from the project list: 6 × 4 × 3 m, six 2,400 lm downlights, 373 lx maintained at the default 0.80 factor. A free account keeps it yours; nothing you draw is uploaded.

Open LuxLab See what's next

v0.10.0 Local-first Metric

LM-63 Type C Direct + up to 4 bounces Metres · lux IndexedDB Ask LuxLab Offline PWA