Isolux fields for the web.
The animated field behind this site, as a library you can use. Floating luminaires light a plane and the iso illuminance lines you choose are traced every frame, merging and splitting as the pools of light meet and part. One call and one JSON object, the way particles.js works.
Your configuration
Only what differs from the defaults. Paste it into your page or save it as JSON for isoluxJS.load.
Light first, then the lines.
Every frame isolux.js lights the plane, traces each level you asked for and draws it as one smooth line.
A real lighting model
Each luminaire is a point source over a horizontal plane: E = I cos θ / d². Pools fall off the way light does, so they meet, merge and part like the plans Lumadex draws.
Exact contours
Marching squares find each level on a grid. The segments are joined into whole lines, every point is moved onto the true contour of the field and each line is drawn as a spline.
Made to sit behind content
It pauses off screen and in background tabs, respects reduced motion, renders sharp on high density screens and follows your light and dark themes.
A few lines to a lit page.
Add it
Download the one file, give the field a sized element and call isoluxJS with the element's id. Settings are optional; a preset name works too.
<div id="isolux"></div>
<script src="../isolux.js"></script>
<script>
isoluxJS("isolux"); // the defaults
isoluxJS("isolux", "road"); // or a preset
isoluxJS("isolux", {
preset: "soft",
contours: { levels: [1, 2, 5, 10] }
});
</script>Load a configuration
Keep the settings in a JSON file, exactly as the playground downloads them.
isoluxJS.load("isolux", "isolux.json", function (field) {
field.on("change", function (luminaires) {
console.log(luminaires.length);
});
});Drive it
Every field is an object you can control.
var field = isoluxDom[0];
field.add(320, 180); // a luminaire, faded in
field.remove(320, 180); // the nearest one, faded out
field.set({ contours: { color: "ink" } });
field.pause(); field.play();
field.export(); // the configuration as JSON
field.destroy();Place them yourself
Fixed positions with a road lantern distribution turn the field into a street lighting plan.
isoluxJS("isolux", {
luminaires: {
positions: [{ x: 0.2, y: 0.4 }, { x: 0.5, y: 0.4 }],
distribution: { type: "road", spread: 2 }
},
move: { enable: false }
});One file, nothing else to install: no dependencies, images or fonts. The MIT licence and a usage guide are in its header.
Everything you can set.
Each option has a default, so a field works with no settings at all. The playground above writes these for you.
| Option | Default | What it does |
|---|---|---|
luminaires.number.value | 7 | How many luminaires float in the field. With density on, scaled to the canvas area. |
luminaires.positions | [] | Place luminaires yourself: x and y as fractions of the canvas (or pixels above 1), with optional intensity, mounting_height and rotate. |
luminaires.mounting_height.value | 80 | Height above the plane in pixels. Low luminaires make tight bright pools; high ones spread wide and soft. |
luminaires.distribution.type | "organic" | The shape of each pool: circle, oval, road, forward, batwing, square, clover, trefoil or organic (every pool its own breathing shape). A list mixes shapes; "random" picks one each; "custom" uses your own values. |
luminaires.distribution.harmonics | per shape | Lobes around a pool as [n, amount, phase]: 2 a batwing, 3 a trefoil, 4 a clover. With spread, throw, power (2 round, 4 square) and morph (lobes drift, in turns per minute) you can draw any distribution. |
move.speed | 0.35 | Travel speed. With move.enable false every luminaire holds still. |
move.drift.amplitude | 36 | A slow orbit around each travelling position, in pixels. |
move.out_mode | "bounce" | "bounce" keeps luminaires near the canvas; "out" lets them leave and return on the far side. |
field.model | "cosine" | "cosine" is a point source over a horizontal plane (I cos θ / d²); "gaussian" is a soft pool without the long tail. |
field.scale | 20 | Illuminance in lux directly below a luminaire of intensity 1. Levels are read against it. |
contours.levels | [1, 2, 3, 5, 10, 15] | The isolux lines to draw, in lux. As pools meet the lines merge; as they part, the lines split. |
contours.color | "spectrum" | "spectrum" (the Lumadex fan from low to high), "ink" (follows the theme), one colour or a list per level. |
contours.fill | enable, opacity 0.14 | A soft glow of the field itself, coloured by band. |
contours.labels | enable, "{value} lx" | A label on the longest runs of each level, aligned with its line, in a clean gap. Labels slide with their line instead of jumping. |
interactivity.detect_on | "canvas" | "window" listens across the page so a field behind your content still responds; ignore lists the elements it should not react over. |
interactivity.events.onhover.mode | "light" | "light" (the cursor carries its own luminaire), "repulse", "attract" or "bubble". |
interactivity.events.onclick.mode | "add" | "add", "remove" or "pulse". Luminaires fade in and out. |
interactivity.events.ondrag.enable | true | Pick up a luminaire and move it. It drifts on from where you drop it. |
theme.mode | "auto" | Follows the page's theme attribute (set in theme.attribute), then the system setting. Each theme has its own ink, background and opacity. |
performance | 60 fps, retina | fps_limit, retina_detect, pause_offscreen and reduced_motion (respects the visitor's setting). |
The same field while Lumadex loads.
Lumadex Desktop shows an isolux field while it starts and while a project opens. It reads the same JSON as isolux.js and draws it natively on its own thread, so the light keeps moving while the project loads.
Opening Riverside car parklumadex-loading.json, the file Lumadex Desktop ships.See the real thing on one of your own sites.
We would welcome the chance to take your team through Lumadex against the Standards you design to. Draw the area and see the first calculation before the survey arrives.