<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Devlog on Lux</title><link>https://uselux.app/devlog/</link><description>Recent content in Devlog on Lux</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://uselux.app/devlog/index.xml" rel="self" type="application/rss+xml"/><item><title>Hello World: Introducing Lux</title><link>https://uselux.app/devlog/hello-world/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/hello-world/</guid><description>Starting Lux, a cross-platform visual programming system for all of us creative coders, installation makers, and visual people. Have to start somewhere.</description></item><item><title>Week One: From Zero to a Running Node Graph</title><link>https://uselux.app/devlog/week-one-from-zero-to-node-graph/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/week-one-from-zero-to-node-graph/</guid><description>First week of Lux. Nodes, pins, wires, a graph evaluator, and 462 tests. Here&amp;rsquo;s how the core engine came together.</description></item><item><title>Pixels on Screen: Building the GPU Pipeline</title><link>https://uselux.app/devlog/pixels-on-screen/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/pixels-on-screen/</guid><description>wgpu + Vello + a blit pipeline. Getting the first shape rendered was harder than expected.</description></item><item><title>The Editor Takes Shape</title><link>https://uselux.app/devlog/the-editor-takes-shape/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-editor-takes-shape/</guid><description>egui panels, a node canvas, pin tooltips, wire highlights. Am I now a UX designer????!!!!</description></item><item><title>Output Modes: Preview, Split, and Multi-Window</title><link>https://uselux.app/devlog/output-modes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/output-modes/</guid><description>Four ways to see your output. Because live performance doesn&amp;rsquo;t care about your editor layout.</description></item><item><title>Spreads: The Core of Lux</title><link>https://uselux.app/devlog/spreads-the-core-of-lux/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/spreads-the-core-of-lux/</guid><description>What if you never wrote a loop again? One wire carries [10, 20, 30, 40, 50]. Every node downstream evaluates per-element automatically.</description></item><item><title>Wires and Undo</title><link>https://uselux.app/devlog/editor-ux-wires-search-undo/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/editor-ux-wires-search-undo/</guid><description>Wire selection with glow, Alt+drag scissors, and undo/redo. The small interactions that make an editor feel real.</description></item><item><title>Search Everywhere</title><link>https://uselux.app/devlog/search-everywhere/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/search-everywhere/</guid><description>Double-tap Shift. Find anything. Create nodes, jump to existing ones, run commands. The fastest way to do anything in Lux.</description></item><item><title>Cleaning House: Architecture, Logging, and 67 New Tests</title><link>https://uselux.app/devlog/cleaning-house/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/cleaning-house/</guid><description>I stopped building features and fixed the foundation. Crate boundaries, structured logging, headless GPU testing.</description></item><item><title>Zero-Allocation Evaluation</title><link>https://uselux.app/devlog/zero-allocation-eval/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/zero-allocation-eval/</guid><description>I tore apart the eval loop and rebuilt it. Pre-allocated buffers, cached adjacency maps, borrowed NodeInfo. No allocations in the hot path.</description></item><item><title>Animation: LFO, Phasor, and Pulse</title><link>https://uselux.app/devlog/animation-oscillators/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/animation-oscillators/</guid><description>The heartbeat of any patch. Three oscillator nodes that make things move.</description></item><item><title>Animation: Damper, Spring, and Easing</title><link>https://uselux.app/devlog/animation-smoothing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/animation-smoothing/</guid><description>Raw data is jagged. These nodes make everything smooth, bouncy, or shaped exactly how you want.</description></item><item><title>Animation: Timing Nodes</title><link>https://uselux.app/devlog/animation-timing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/animation-timing/</guid><description>Timer, Metro, Delay, Debounce, Throttle, OnChange. When things happen matters as much as what happens.</description></item><item><title>Animation: Sequencing and Control Flow</title><link>https://uselux.app/devlog/animation-sequencing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/animation-sequencing/</guid><description>Counter, Select, Switch, Toggle, FlipFlop, Sequencer, SampleAndHold. The logic layer that ties animation together.</description></item><item><title>42 Math and Logic Nodes</title><link>https://uselux.app/devlog/math-nodes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/math-nodes/</guid><description>Add, Subtract, Multiply, Divide, Sin, Cos, Lerp, SmoothStep, Map, and 33 more. Polymorphic, type-preserving, NaN-safe.</description></item><item><title>Animation: FrameDelay, StateMachine, and Timeline</title><link>https://uselux.app/devlog/animation-state/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/animation-state/</guid><description>Memory across frames. Feedback loops, named state transitions, and keyframed values.</description></item><item><title>Logic and Comparison Nodes</title><link>https://uselux.app/devlog/logic-nodes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/logic-nodes/</guid><description>Equal, Greater, Less, InRange, And, Or, Not, Xor. The decision-making layer of the node graph.</description></item><item><title>Every Node, Documented</title><link>https://uselux.app/devlog/every-node-documented/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/every-node-documented/</guid><description>110 nodes and 203 pins now carry structured documentation — summaries, descriptions, tags, cross-references, and per-pin help text — all the way from source code to the editor UI and a machine-readable JSON export.</description></item><item><title>Editor Polish, Round One</title><link>https://uselux.app/devlog/editor-polish-round-one/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/editor-polish-round-one/</guid><description>Proper z-ordering, smarter tooltips, a disappearing inspector, search that opens where you&amp;rsquo;re looking, and a theme system that replaces every magic number.</description></item><item><title>Filter: The Missing Spread Node</title><link>https://uselux.app/devlog/filter-node/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/filter-node/</guid><description>A new Filter node lets you keep only the elements you want from a spread using a boolean mask. Wire a comparison node into the mask and you&amp;rsquo;ve got conditional data flow.</description></item><item><title>Shapes, Docs, and Polish</title><link>https://uselux.app/devlog/shapes-docs-and-polish/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/shapes-docs-and-polish/</guid><description>A big session: 6 new shape primitives, inline documentation for every node and 203 pins, smarter tooltips, z-ordered nodes, search-at-cursor, a Filter node, and an adversarial review that caught 14 issues before users could.</description></item><item><title>The Cleanup Episode</title><link>https://uselux.app/devlog/the-cleanup-episode/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-cleanup-episode/</guid><description>A deep code review found 39 issues, a 274KB font was being copied every frame, circles were made of 360 line segments, and five unwrap() calls were lurking in the render pipeline. We fixed all of it.</description></item><item><title>Stop Cloning Everything</title><link>https://uselux.app/devlog/stop-cloning-everything/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/stop-cloning-everything/</guid><description>Four performance improvements that boil down to one lesson: stop copying things you don&amp;rsquo;t need to copy. Arc-wrapped layers, zero-alloc output maps, borrowed spread slices, and a wire drag fix.</description></item><item><title>GPU Texture Pipeline</title><link>https://uselux.app/devlog/gpu-texture-pipeline/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/gpu-texture-pipeline/</guid><description>Lux gets a GPU-resident texture processing engine — handle-based allocation, free-list pooling, shader caching, and a declarative op system that keeps plugins completely decoupled from wgpu.</description></item><item><title>Texture Sources</title><link>https://uselux.app/devlog/texture-sources/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/texture-sources/</guid><description>Three nodes that create textures from nothing — LoadImage for files, SolidColor for flat fills, and NoiseTexture for procedural Perlin, Simplex, and Worley patterns.</description></item><item><title>Texture Filters</title><link>https://uselux.app/devlog/texture-filters/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/texture-filters/</guid><description>Eight GPU filter nodes — Blur, Brightness, Contrast, Invert, Threshold, HueShift, Saturate, EdgeDetect — each a WGSL shader wrapped in a single Rust file.</description></item><item><title>Layer ↔ Texture Bridge</title><link>https://uselux.app/devlog/layer-texture-bridge/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/layer-texture-bridge/</guid><description>Two nodes that bridge Lux&amp;rsquo;s two rendering worlds — RenderTarget converts 2D vector layers into GPU textures, TextureToLayer brings them back.</description></item><item><title>Feedback Loops</title><link>https://uselux.app/devlog/feedback-loops/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/feedback-loops/</guid><description>A Feedback node that composites the previous frame with the current one — trails, echoes, and accumulation effects, plus the GC trick that makes it possible.</description></item><item><title>Custom WGSL Shaders</title><link>https://uselux.app/devlog/custom-wgsl-shaders/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/custom-wgsl-shaders/</guid><description>PixelShader and ComputeShader nodes that let users write custom WGSL with auto-detected uniforms, dynamic input pins, and a hand-rolled shader parser that makes it all work.</description></item><item><title>Extended Texture Processing</title><link>https://uselux.app/devlog/extended-texture-processing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/extended-texture-processing/</guid><description>Seventeen new texture nodes across four crates — transforms, compositing, analysis, and post-FX including bloom, chroma key, tone mapping, and a seven-mode blend node.</description></item><item><title>Video Playback</title><link>https://uselux.app/devlog/video-playback/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/video-playback/</guid><description>A VideoPlayer node with feature-gated FFmpeg decoding — MP4, MOV, WebM, anything FFmpeg supports — with seeking, looping, variable rate, and frame caching.</description></item><item><title>Finishing Phase 7</title><link>https://uselux.app/devlog/finishing-phase-7/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/finishing-phase-7/</guid><description>Six nodes the spec was missing, two GPU memory leaks, and a pile of small texture-engine fixes. The texture pipeline is finally done.</description></item><item><title>Smoothing, but for Everything</title><link>https://uselux.app/devlog/smoothing-for-everything/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/smoothing-for-everything/</guid><description>Damper, Spring, SmoothDamp, Ease, and Ramp used to only speak Number. Now they handle Vec2, Vec3, Vec4, and Color, component-wise, with output types that match whatever you plug in.</description></item><item><title>Save and Load: The .lux File Format</title><link>https://uselux.app/devlog/project-files/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/project-files/</guid><description>Ctrl+S, Ctrl+O, a proper .lux project format, and one sneaky bug where nodes were invisible after loading because the render cache didn&amp;rsquo;t know anything had changed.</description></item><item><title>The Create Dialog and a UX Round</title><link>https://uselux.app/devlog/the-create-dialog/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-create-dialog/</guid><description>Spacebar opens a tabbed node creator. Plus four UX changes in one commit: richer inspector widgets, undo coalescing, copy/paste, and wire depth fading.</description></item><item><title>Export: Stills, Sequences, and a Real Output Resolution</title><link>https://uselux.app/devlog/export-and-resolution/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/export-and-resolution/</guid><description>Ctrl+E for a screenshot. Ctrl+Shift+E for a 150-frame sequence. An ExportImage node for programmatic captures. And an output resolution system that decouples what you render from what the window happens to be.</description></item><item><title>Clean UI: Inspector on Click, Lucide, JetBrains Mono</title><link>https://uselux.app/devlog/clean-ui/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/clean-ui/</guid><description>The editor used to have two permanent panels eating screen space. Now it has none. Plus a typography overhaul. Lucide icons and JetBrains Mono throughout.</description></item><item><title>Merge, Multi-Wire, and the Death of Group Chains</title><link>https://uselux.app/devlog/merge-and-multi-wire/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/merge-and-multi-wire/</guid><description>One node that takes any number of layers. A graph engine that accepts multiple wires into a single Spread input. Drag-to-reorder layer ordering in the inspector. The ugly Group-chain pattern is finally gone.</description></item><item><title>Zero GPU Allocations Per Frame</title><link>https://uselux.app/devlog/zero-gpu-allocations/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/zero-gpu-allocations/</guid><description>The texture engine was allocating GPU resources every frame. Now it isn&amp;rsquo;t. Plus a stack-allocated path for wire curves, a single command encoder per frame, and 16 tests that were overdue.</description></item><item><title>The Trust Pass: Autosave, Async, and a Progress Bar</title><link>https://uselux.app/devlog/the-trust-pass/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-trust-pass/</guid><description>Lux used to freeze when you loaded an image, freeze when you exported a sequence, and never tell you whether you had unsaved changes. This session fixed all of that, plus moved SolidColor and NoiseTexture fully onto the GPU.</description></item><item><title>Lux Goes 3D</title><link>https://uselux.app/devlog/lux-goes-3d/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/lux-goes-3d/</guid><description>Phase 9 starts here. A new render_3d pass, a mesh pool with content-addressed upload, three new pin types, and a plugin that does exactly one thing: draw a triangle.</description></item><item><title>Boxes, Spheres, and Something to Point at Them</title><link>https://uselux.app/devlog/boxes-spheres-and-something-to-point-at-them/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/boxes-spheres-and-something-to-point-at-them/</guid><description>The scene you can actually build. Box, Sphere, Plane, Grid, a transform stack, a perspective camera, and orbit controls. Then the cleanup pass that rolled back half the decisions from the last post.</description></item><item><title>Lights, Materials, and the Sphere That Wasn't There</title><link>https://uselux.app/devlog/lights-materials-and-the-sphere-that-wasnt-there/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/lights-materials-and-the-sphere-that-wasnt-there/</guid><description>Lambert and Phong, directional and point and ambient lights, a std140 LightBlock, and six hours of debugging an invisible sphere because I wound every triangle the wrong way.</description></item><item><title>10,000 Cubes in One Draw Call</title><link>https://uselux.app/devlog/ten-thousand-cubes-in-one-draw-call/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/ten-thousand-cubes-in-one-draw-call/</guid><description>GPU instancing, a real BufferPool behind it, content-hashed transform uploads, and the stale-handle bug that took the whole thing down until I figured out which node had to own the keep-alive.</description></item><item><title>Compute Shaders Get Real Buffers</title><link>https://uselux.app/devlog/compute-shaders-get-real-buffers/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/compute-shaders-get-real-buffers/</guid><description>A new compute pipeline cache that can read and write N storage buffers at arbitrary bindings. No visible output, but every GPU-side node from here on depends on it.</description></item><item><title>Prism</title><link>https://uselux.app/devlog/prism/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/prism/</guid><description>The idea at the heart of Lux, and the reason I started building it in the first place. Shaders are already graphs of smaller functions. So why are we still writing them as text?</description></item><item><title>SDFs Without Writing Shaders</title><link>https://uselux.app/devlog/sdfs-without-writing-shaders/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/sdfs-without-writing-shaders/</guid><description>A graph of SDF nodes stitched into a single WGSL fragment shader at evaluation time. Drop a circle, drop a box, drop a smooth union, see the result raymarched to the screen. No text editor involved.</description></item><item><title>Particles, Finally</title><link>https://uselux.app/devlog/particles-finally/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/particles-finally/</guid><description>600 particles under gravity, an Emitter2D monolith with fourteen input pins, and a new &amp;ndash;warmup CLI flag because headless snapshots of stateful nodes were broken in a way I hadn&amp;rsquo;t noticed.</description></item><item><title>Everything HDR</title><link>https://uselux.app/devlog/everything-hdr/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/everything-hdr/</guid><description>A new default texture format, a dedicated tonemap pass at the edge of the scene, and a pipeline that finally stops crushing every value above 1.0.</description></item><item><title>The Framegraph</title><link>https://uselux.app/devlog/the-framegraph/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-framegraph/</guid><description>A new crate that owns the compile-time phase of the Lux render pipeline. Passes declare what they read and write, the compiler aliases transient textures, and bloom goes from three independent dispatches to one dependency-ordered chain.</description></item><item><title>Post-FX: Bloom, TAA, CAS, and Film Grain</title><link>https://uselux.app/devlog/post-fx-stack/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/post-fx-stack/</guid><description>Four post-process passes, each opt-in per scene, each written against the new HDR buffer and the new framegraph. Jimenez bloom, depth-only reprojection TAA, AMD FidelityFX CAS, and a compute-hash grain pass.</description></item><item><title>Cook-Torrance PBR + Normal Maps</title><link>https://uselux.app/devlog/cook-torrance-pbr/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/cook-torrance-pbr/</guid><description>A real physically-based shading model lands. Metallic/roughness workflow, GGX microfacets, Schlick Fresnel, normal maps with per-vertex tangents, and one SceneObject node that bundles geometry and material.</description></item><item><title>Image-Based Lighting</title><link>https://uselux.app/devlog/image-based-lighting/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/image-based-lighting/</guid><description>A sphere lit only by an environment map. Four compute passes — environment cubemap, irradiance, prefiltered mip chain, BRDF LUT — plus the split-sum sampling in the PBR shader that makes it all converge.</description></item><item><title>More 3D, More Filters</title><link>https://uselux.app/devlog/more-3d-more-filters/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/more-3d-more-filters/</guid><description>The cleanup post. Three new 3D primitives (Cylinder, Cone, Torus), a Twist3D mesh deformer, a SRT gizmo, plus five small texture nodes that shipped alongside the big render rewrite.</description></item><item><title>Async Readback and the StagingBelt</title><link>https://uselux.app/devlog/async-readback-and-staging-belt/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/async-readback-and-staging-belt/</guid><description>Two render-layer perf fixes that each closed a sync stall on the hot path. The GPU readback loop stops blocking on device.poll; uniform writes stop going through individual queue.write_buffer calls. Net: 3 ms of frame time I didn&amp;rsquo;t know I was leaking.</description></item><item><title>The First 60 Seconds</title><link>https://uselux.app/devlog/the-first-60-seconds/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-first-60-seconds/</guid><description>A welcome splash, a Mouse-to-Circle sample patch, a menu bar, a help cheatsheet, an empty-canvas CTA, and a preferences file. The moves that close the gap between &amp;lsquo;installs&amp;rsquo; and &amp;lsquo;starts using it&amp;rsquo;.</description></item><item><title>Wires That Help You</title><link>https://uselux.app/devlog/wires-that-help-you/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/wires-that-help-you/</guid><description>Magnet-snap to the nearest compatible pin. Drop a wire into empty canvas and get a filtered, pin-type-aware search. Backward wires that route around their source node instead of through it. Plus one central place where every editor animation lives.</description></item><item><title>Tooltips, Error Dots, and the F8 Profiler</title><link>https://uselux.app/devlog/tooltips-errors-and-the-profiler/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/tooltips-errors-and-the-profiler/</guid><description>Pin tooltips that show defaults and ranges. A red error dot on any node that panicked during evaluation. An F8 profiler HUD that makes frame time a number instead of a vibe. Plus search that indexes summaries and tags.</description></item><item><title>The Graph Engine Rewrite: Why</title><link>https://uselux.app/devlog/graph-engine-rewrite-why/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/graph-engine-rewrite-why/</guid><description>The uncomfortable part of shipping a fast renderer is discovering that none of your benchmarks measured the actual frame. A baseline audit, a list of structural problems, and the set of P-gates the rewrite has to clear.</description></item><item><title>SlotMap + Generation Counters</title><link>https://uselux.app/devlog/slotmap-and-generation-counters/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/slotmap-and-generation-counters/</guid><description>The first landing of the graph engine rewrite. Seven parallel HashMaps collapse into one packed NodeSlot per node, keyed by a DenseSlotMap. Dirty checking moves from deep-compare to per-pin generation counters.</description></item><item><title>PinId: The Death of HashMap&lt;String, _></title><link>https://uselux.app/devlog/pin-id-death-of-string-maps/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/pin-id-death-of-string-maps/</guid><description>Every pin lookup used to rehash its name. After this post, every pin lookup is an array index. A 16-bit PinId, a proc-macro that generates constants per node, a ProcessContext overload, and 0 string allocations per frame at 1000 nodes.</description></item><item><title>Breaking Up LuxApp</title><link>https://uselux.app/devlog/breaking-up-luxapp/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/breaking-up-luxapp/</guid><description>LuxApp was 2,200 lines of everything talking to everything. Six refactor commits later it&amp;rsquo;s seven modules with single responsibilities, a unified EditorAction queue, and an egui_dock scaffold where floating panels used to be.</description></item><item><title>Pearce-Kelly Dynamic Topological Sort</title><link>https://uselux.app/devlog/pearce-kelly-topo-sort/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/pearce-kelly-topo-sort/</guid><description>Every wire edit used to re-sort the entire graph from scratch. 180 µs per connect at 1000 nodes. Now the common case is O(1) and the worst case is O(|δ|) — the size of the affected region, not the size of the graph.</description></item><item><title>Parallel Evaluation with Rayon</title><link>https://uselux.app/devlog/parallel-evaluation-with-rayon/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/parallel-evaluation-with-rayon/</guid><description>The evaluator was running on one core. The graph is a DAG, which means huge chunks of it can run in parallel. A level partition + rayon per-level par_iter and the 1000-node fan-out benchmark drops to half its previous cost.</description></item><item><title>Spread Becomes Arc&lt;[T]></title><link>https://uselux.app/devlog/spread-becomes-arc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/spread-becomes-arc/</guid><description>A 1000-element spread used to deep-clone every time it crossed a wire. 50 hops through a graph meant 50,000 clones per frame. Replacing Spread(Vec&lt;PinValue>) with Spread(Arc&amp;lt;[PinValue]&amp;gt;) collapses the wire-hop cost to one atomic refcount bump — a 420× speedup on the benchmark that matters most.</description></item><item><title>Bit-Identical Goldens + iai-callgrind</title><link>https://uselux.app/devlog/bit-identical-goldens/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/bit-identical-goldens/</guid><description>The rewrite is allowed to be fast. It is not allowed to change a single pixel. A stricter-than-family golden test suite locks down every pre-rewrite test patch to SSIM ≥ 0.9999 and max 1-LSB diff. Plus iai-callgrind companion benches to put CPU-instruction-count regression gates in CI.</description></item><item><title>The Last Few Allocations</title><link>https://uselux.app/devlog/last-few-allocations/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/last-few-allocations/</guid><description>Three final perf passes on the graph engine. Wire transfer that skips the gen-bump when the value didn&amp;rsquo;t change. A per-target connection index so wire lookups are O(indegree) instead of O(edges). A processed-set that short-circuits predecessors already visited this frame. Together: 886 µs → under 10 µs on the unchanged-subtree bench.</description></item><item><title>Projection Mapping</title><link>https://uselux.app/devlog/projection-mapping/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/projection-mapping/</guid><description>Three new nodes that live between your output and the wall you&amp;rsquo;re projecting on. Corner-pin warp with a DLT homography solver. A mesh warp driven by an editable bezier grid. Edge blending for projector stacks. Plus the canvas gizmos that make all three clickable.</description></item><item><title>Kornia-Class Image Analysis</title><link>https://uselux.app/devlog/kornia-class-image-analysis/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/kornia-class-image-analysis/</guid><description>Sixteen new analysis nodes plus two migrated ones: Sobel, Laplacian, Canny, morphological ops, histograms, SSIM/PSNR/MSE/MAE, FFT and IFFT, Lucas-Kanade optical flow. The computer-vision toolbox Lux didn&amp;rsquo;t have, now as GPU-accelerated node-graph citizens.</description></item><item><title>Multi-Window, HDR Toggle, Encode Queue</title><link>https://uselux.app/devlog/multi-window-hdr-encode-queue/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/multi-window-hdr-encode-queue/</guid><description>Three new systems for the live-performance side of Lux. A window fleet that manages up to 16 output surfaces with a 50% VRAM cap. A live HDR toggle that survives monitor hot-plug. A dedicated encoder thread that records while you perform without stalling the render.</description></item><item><title>PerfGuard, Hitch Capture, Crash Sandbox</title><link>https://uselux.app/devlog/perfguard-hitch-crash-sandbox/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/perfguard-hitch-crash-sandbox/</guid><description>When something goes wrong mid-performance, the show cannot stop. An eight-step fallback ladder that degrades render quality to stay on budget. A 32-slot hitch-capture ring for post-mortem analysis. A crash sandbox that catches panicking nodes, dead devices, and malformed shaders without taking the app down.</description></item><item><title>ReSTIR + Denoise</title><link>https://uselux.app/devlog/restir-and-denoise/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/restir-and-denoise/</guid><description>Real-time global illumination finally lands. ReSTIR direct + indirect for sampling a scene lit by many lights. Three denoisers (SVGF, ReBLUR, Hybrid) for cleaning up the noisy result. A new texture-screen-space plugin crate that wires all of it together as nodes.</description></item><item><title>Built, Not Adopted</title><link>https://uselux.app/devlog/built-not-adopted/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/built-not-adopted/</guid><description>A pile of well-tested library code that the app wasn&amp;rsquo;t actually calling. BindGroupCache was used by 1 of 34 sites. The MasterClock was constructed zero times. The welcome modal had eleven cards and one of them worked. Twenty-two items of cleanup that close the gap between &amp;lsquo;shipped&amp;rsquo; and &amp;lsquo;wired.&amp;rsquo;</description></item><item><title>ResourceKind Becomes a Closed Set</title><link>https://uselux.app/devlog/resourcekind-becomes-a-closed-set/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/resourcekind-becomes-a-closed-set/</guid><description>The framegraph shipped a year ago with one resource variant and a comment that said the others were coming. They were not coming. They were waiting. This post is them arriving (Texture2D, Depth, Cube, Texture3D, TextureArray, Msaa, Buffer), plus the queue-affinity tags that make compute dispatch routable, the cross-queue barrier that goes on the destination encoder, and the 2,688-line god-file that finally got carved up.</description></item><item><title>Every Shade Path Imports lux::pbr</title><link>https://uselux.app/devlog/every-shade-path-imports-pbr/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/every-shade-path-imports-pbr/</guid><description>I wrote a careful PBR module months ago and the live mesh shader was bypassing it the whole time. A chrome sphere at roughness 0.5 was losing 12 to 18 percent of its incident energy. Plus: the BRDF LUT going from 2 channels to 4, the legacy ToneMap plugin getting deleted, every plugin shader flipping from Rec.601 to Rec.709, and ColorGrade3DLUT going from &lt;code>return src&lt;/code> to actually grading.</description></item><item><title>Shadow Orchestrator + 1024-Slot Lights</title><link>https://uselux.app/devlog/shadow-orchestrator-and-1024-lights/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/shadow-orchestrator-and-1024-lights/</guid><description>The live shadow path was a hardcoded ±10 ortho, an 0.005 magic-number bias, an atlas the size of someone&amp;rsquo;s first attempt, and an 8-slot light uniform that silently dropped lights past slot 7. Worse: every area light and IES light was packing as LIGHT_TYPE_NONE = 0 and getting dropped by the shader&amp;rsquo;s guard before it ever reached the loop. Now there&amp;rsquo;s an orchestrator with cascade-snap stability, a 4-cascade PSSM, a 1024-slot LightStore, and a cluster-bin compute pass that puts the lights in tiles instead of a fixed-size uniform.</description></item><item><title>The Meshlet Path, So Far</title><link>https://uselux.app/devlog/the-meshlet-path-so-far/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-meshlet-path-so-far/</guid><description>The bindless mesh shader is 778 carefully-authored lines of WGSL that the pipeline doesn&amp;rsquo;t load yet, because the pipeline still loads a 3-line placeholder that draws degenerate triangles. Effective. Pass 1 of the meshlet rewrite landed (cull-ratio gates: 85 percent on a stadium, 0 false rejections on a fully-visible scene). Passes 2 through 6 are queued. This post is the status dispatch.</description></item><item><title>The Debt Sweep</title><link>https://uselux.app/devlog/the-debt-sweep/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-debt-sweep/</guid><description>Built, Not Adopted was the wiring half of the cleanup. This is the deletion half. A #[deprecated] Material shim, an 845-line version-migration module, a legacy Cargo feature, and the last hand-rolled fallback in the node registry, all gone. Plus the #[lux_node] macro migration finishing across 233 nodes in 42 plugin crates.</description></item><item><title>The Train Rides the Rails</title><link>https://uselux.app/devlog/the-train-rides-the-rails/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-train-rides-the-rails/</guid><description>The framegraph post shipped queue-affinity routing and then admitted the live orchestrator still ran everything on one encoder. Rails, not the train. This post flips it: a graphics and compute encoder pair per frame, run_with_dispatch on the live path, and the two 1x1 sentinel textures that were impersonating GPU buffers finally deleted.</description></item><item><title>The Skinning Fraud, Closed</title><link>https://uselux.app/devlog/the-skinning-fraud-closed/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-skinning-fraud-closed/</guid><description>Built, Not Adopted admitted the GPU skinning compute shader was complete, tested, and dispatching, and that the live renderer ignored all of it and ran a CPU loop instead. This post closes that. DrawItem becomes an enum, the renderer consumes the GPU-deformed buffer, and skin_cpu (function, Cargo feature, and all) is deleted.</description></item><item><title>The Bindless Arm Goes Live</title><link>https://uselux.app/devlog/the-bindless-arm-goes-live/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-bindless-arm-goes-live/</guid><description>For most of Lux&amp;rsquo;s life the renderer announced every object to the GPU one at a time, which is fine until a scene has a few thousand of them and the CPU falls over. The bindless arm draws the entire scene in a single GPU-driven indirect call and lets the GPU decide what is visible, which is the road to dense scenes at framerate. This post loads the real 778-line shader, stands up the orchestrator that feeds it the whole scene, caches its seven bind groups, and flips the live arm on capable hardware, keeping the old renderer beside it on purpose so the new one can be proven correct before anything is deleted.</description></item><item><title>Deleting the Legacy Renderer</title><link>https://uselux.app/devlog/deleting-the-legacy-renderer/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/deleting-the-legacy-renderer/</guid><description>Lux had two mesh renderers: a fast bindless one that drew static geometry, and a 1,466-line legacy function that did everything else (shadows, skinned characters, instances). Two renderers is a slow fork in every frame and two ways for your scene to drift. This post teaches the fast path everything the slow one knew, deletes the slow one, and then solves the harder problem the deletion forces: with no old renderer to compare against, correctness stops meaning &amp;lsquo;matches last quarter&amp;rsquo; and starts meaning &amp;lsquo;matches physics&amp;rsquo;, which is the ruler that actually protects what you see.</description></item><item><title>Carving the Core</title><link>https://uselux.app/devlog/carving-the-core/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/carving-the-core/</guid><description>A rewrite the size of the bindless mesh path leaves the codebase a little god-shaped, and a god-shaped codebase is how a tool that demos beautifully starts hitching in your hands. This is the cleanup that keeps that from happening: the Render3D framegraph collapsed into one graph (so your shadows actually render and the async-compute overlap actually fires), thirty-plus per-frame allocations routed through the bind-group cache, and a 3,466-line bridge file carved into eight, with a workspace-wide sweep behind it.</description></item><item><title>The 800-Line Ceiling</title><link>https://uselux.app/devlog/800-line-ceiling/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/800-line-ceiling/</guid><description>You will never see this commit, and that is the point. The god-file carve is finished, and a CI gate now stops any engine file from crossing 800 lines again. Why it matters to you: a codebase that does not rot is the difference between a tool that gets better every week and one that slows to a crawl, and small, focused files are what keep the per-frame hot path allocation-free when your patch gets big.</description></item><item><title>The Shading Path That Never Shaded</title><link>https://uselux.app/devlog/shading-path-that-never-shaded/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/shading-path-that-never-shaded/</guid><description>Lux had two ways to light a surface, and only one of them was real. The other shaded everything magenta and never drew a frame. Two lighting paths means two ways for your materials to look wrong, so this post deletes the dead one. Now there is exactly one place where light meets a surface, which means every tonemap tweak, every roughness fix, every colour improvement lands in every scene at once. Your gold looks like gold everywhere.</description></item><item><title>The Orphan a Deletion Leaves</title><link>https://uselux.app/devlog/orphan-a-deletion-leaves/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/orphan-a-deletion-leaves/</guid><description>Deleting the dead deferred renderer left a ghost behind: a GPU classifier whose every caller had just been deleted. This is the short one about chasing a deletion all the way to zero, and about a cost you pay without seeing it. Every line of &amp;lsquo;keep it, might be useful later&amp;rsquo; is load time, memory, and a place for bugs to hide. The engine you run should be exactly the engine that is alive.</description></item><item><title>The Default Look, Done by the Book</title><link>https://uselux.app/devlog/the-default-look-done-by-the-book/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-default-look-done-by-the-book/</guid><description>The tonemap is the last thing that touches your render: the curve that turns raw HDR light into the image you actually see. The default was quietly wrong, mid-grey too bright and bright colours drifting. It now runs the real ACES 2.0 transform, the same one film and VFX pipelines use, matched to the reference closely enough that the difference is invisible. Your default 3D look is now the industry&amp;rsquo;s default 3D look, out of the box.</description></item><item><title>The Models Arrive Dressed</title><link>https://uselux.app/devlog/the-models-arrive-dressed/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-models-arrive-dressed/</guid><description>Drop a glTF model into Lux and it now shows up wearing its own textures: the helmet has its scratches and decals, the wood looks like wood, the character keeps its skin. Until now every imported model rendered as a flat plastic version of itself, because the importer read the texture references and then quietly threw them away. Now it routes them all the way to the GPU, in the right colour space, for every channel.</description></item><item><title>Light Learns to Bounce</title><link>https://uselux.app/devlog/light-learns-to-bounce/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/light-learns-to-bounce/</guid><description>Surfaces in Lux now catch the colour of whatever sits next to them. Put a white object beside a red wall and its facing side picks up a warm red blush, the way real light spills off one surface onto the next. It is Lux&amp;rsquo;s first global illumination: indirect, coloured, and aware of which way a surface is pointing.</description></item><item><title>Two Keys Come Home</title><link>https://uselux.app/devlog/two-keys-come-home/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/two-keys-come-home/</guid><description>Space opens the node browser and Tab opens fuzzy search, exactly as the empty canvas has been promising all along. The two fastest routes from idea to node now actually exist under your fingers, and the cue transport politely waits for its own panel before borrowing the space bar.</description></item><item><title>Stars in the Window</title><link>https://uselux.app/devlog/stars-in-the-window/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/stars-in-the-window/</guid><description>Generative content finally shows up in the live editor window, not just in exports. The display cache now tracks textures by what they are rather than what they were called this frame, and the texture pool stops buying a new texture every frame and throwing it away.</description></item><item><title>The Show Must Go On</title><link>https://uselux.app/devlog/the-show-must-go-on/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-show-must-go-on/</guid><description>Adding a node can no longer blank your output, the patch output is now chosen deterministically, and a new selector in the menu bar lets you pin any sink as the show while the driving node wears a little on-air lamp.</description></item><item><title>An Honest Readback</title><link>https://uselux.app/devlog/an-honest-readback/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/an-honest-readback/</guid><description>Heavy scenes now export reliably on NVIDIA hardware, and a failed frame capture says so out loud instead of handing you an empty image. The GPU readback path stops returning nothing and calling it success.</description></item><item><title>A CLI With Manners</title><link>https://uselux.app/devlog/a-cli-with-manners/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/a-cli-with-manners/</guid><description>Typo a flag and Lux now tells you so instead of opening a window on your display. There is a real &amp;ndash;help, headless dumps honour your patch&amp;rsquo;s declared resolution, and &amp;ndash;dump-editor works on its own.</description></item><item><title>The Fog Machine Was a Prop</title><link>https://uselux.app/devlog/the-fog-machine-was-a-prop/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-fog-machine-was-a-prop/</guid><description>The volumetric fog system now actually computes volumetric fog every frame, verified against the physics it claims to implement. No new pixels yet, that lands with the compositing pass, but the foundation underneath has stopped being scenery.</description></item><item><title>Fog That Knows Where Things Are</title><link>https://uselux.app/devlog/fog-that-knows-where-things-are/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/fog-that-knows-where-things-are/</guid><description>Volumetric fog now composites into the frame for real: geometry dissolves into it at the right distances, near objects punch through it, and the density knob is connected to physics instead of a background tint. The six-week-old placeholder tint is gone.</description></item><item><title>Clouds With Actual Shapes</title><link>https://uselux.app/devlog/clouds-with-actual-shapes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/clouds-with-actual-shapes/</guid><description>The volumetric cloud system now traces, accumulates and composites real cumulus into your scene: billowing shapes with shadowed bases and gaps of sky, masked behind your geometry, driven by the coverage and density pins. Clouds also no longer require wiring a sky first.</description></item><item><title>Three Tables Find an Audience</title><link>https://uselux.app/devlog/three-tables-find-an-audience/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/three-tables-find-an-audience/</guid><description>The atmosphere model has been computing a physically based sky every frame and throwing it away. A new sky pass finally draws it: horizon gradients, a sun disc filtered through the atmosphere, and a sun elevation pin that behaves like the actual sun.</description></item><item><title>Turning the Wind Up to Eighteen</title><link>https://uselux.app/devlog/turning-the-wind-up-to-eighteen/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/turning-the-wind-up-to-eighteen/</guid><description>The WaterSurface node now drives a real Tessendorf FFT ocean: a JONSWAP spectrum, four wave cascades, choppy crests, whitecap foam, and a displaced surface that depth-sorts against your scene. Set wind to 1 and get a pond; set it to 18 and get a storm.</description></item><item><title>Sunlight on the Pool Floor</title><link>https://uselux.app/devlog/sunlight-on-the-pool-floor/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/sunlight-on-the-pool-floor/</guid><description>Water is transparent now: look down through the surface and the sea floor is there, refracted, colour-shifted by depth, and dappled with real caustics driven by the wave maths. RiverFlow makes the whole field travel and churn. Two more nodes stop being decorative.</description></item><item><title>The Case of the Vertical Floor</title><link>https://uselux.app/devlog/the-case-of-the-vertical-floor/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-case-of-the-vertical-floor/</guid><description>A reported lighting collapse turns out to be a detective story: a test floor that had been standing on edge the whole time, a patch loader that swallowed typos, eleven bounce-light nodes that never existed at runtime, and one real shadow bug fixed at the end of it. Sunlight now survives every compass direction.</description></item><item><title>Every Light Pulls Its Weight</title><link>https://uselux.app/devlog/every-light-pulls-its-weight/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/every-light-pulls-its-weight/</guid><description>Point and spot lights finally cast shadows. Cube shadows for points, cone views for spots, scheduled on a per-frame budget so a 200-light club rig keeps its framerate. Also the story of a bias that quietly deleted every shadow past the midpoint of a light&amp;rsquo;s range, caught by the test that shipped with the feature.</description></item><item><title>Lights With Actual Shapes</title><link>https://uselux.app/devlog/lights-with-actual-shapes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/lights-with-actual-shapes/</guid><description>Area lights stop pretending to be points: rectangles, disks, spheres, and tubes now integrate their real geometry, ported straight from the canonical LTC reference. IES photometric profiles finally shape beams like the fixture datasheet says. Also the case of a rectangle and a sphere that rendered pixel-identically for months.</description></item><item><title>A Thousand Lights, Six Times Faster</title><link>https://uselux.app/devlog/a-thousand-lights-six-times-faster/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/a-thousand-lights-six-times-faster/</guid><description>The 1000-light stress scene drops from 25 ms to 4 ms a frame on an RTX 5070. The culprit was not the lights themselves but the bounce-light cache politely interviewing every single light for every ray it traced. It now samples sixteen of them and trusts statistics, which is what statistics are for.</description></item><item><title>The Shadow System That Never Was</title><link>https://uselux.app/devlog/the-shadow-system-that-never-was/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-shadow-system-that-never-was/</guid><description>Four thousand lines of virtual shadow map infrastructure leave the tree without a single pixel ever having missed them. A spring-clean release: the shadows you can see stay exactly as they are, and the shadow system that never rendered anything stops slowing down the ones that do.</description></item><item><title>Shadows Learn About Distance</title><link>https://uselux.app/devlog/shadows-learn-about-distance/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/shadows-learn-about-distance/</guid><description>Sun shadows now soften with occluder distance: crisp where a pole meets the ground, increasingly soft toward the tip of its shadow, the way actual sunlight behaves. The shadow_filter pin on every light node finally does something, with four filters to pick from.</description></item><item><title>Motion Vectors Clock In</title><link>https://uselux.app/devlog/motion-vectors-clock-in/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/motion-vectors-clock-in/</guid><description>Temporal anti-aliasing is now on by default, and it finally reads the per-object motion vectors the renderer has been producing all along. Spinning, sliding, and skinned things get smooth edges instead of ghost trails. You change nothing; your edges just stop being staircases.</description></item><item><title>The Resolution Dial</title><link>https://uselux.app/devlog/the-resolution-dial/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-resolution-dial/</guid><description>RenderScene grows an upscale_mode pin: render the 3D pass at 50, 67, or 75 percent resolution and a temporal upscaler reconstructs full size from jitter, history, and motion vectors. The Upscale node, meanwhile, now admits to being a bicubic filter.</description></item><item><title>Glitter With a Leash</title><link>https://uselux.app/devlog/glitter-with-a-leash/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/glitter-with-a-leash/</guid><description>Glossy normal-mapped surfaces stop firefly-sparkling under motion. The Kaplanyan specular anti-aliasing module that has been sitting in the shader library since the design phase is now wired into both mesh shaders, widening the highlight exactly where the pixel can&amp;rsquo;t resolve the bumps.</description></item><item><title>Seven Dead Lobes</title><link>https://uselux.app/devlog/seven-dead-lobes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/seven-dead-lobes/</guid><description>The BSDF node stack finally reaches the GPU: clearcoat, sheen, iridescence, anisotropy, subsurface, transmission and volume parameters now produce pixels instead of compiling into a side table nobody read. Velvet rims, second highlights, brushed bands and oil-slick colours, live.</description></item><item><title>The Fine Print Audit</title><link>https://uselux.app/devlog/the-fine-print-audit/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-fine-print-audit/</guid><description>Every material node&amp;rsquo;s documentation now survives cross-examination: a permanent gate bans claims of physics the shader doesn&amp;rsquo;t run. Brushed metal also learns to bend its environment reflections along the grain, replacing a flat brightness fudge.</description></item><item><title>Glass, At Last</title><link>https://uselux.app/devlog/glass-at-last/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/glass-at-last/</guid><description>Lux gets its first real transparency: transmissive materials render as actual see-through glass, sorted back-to-front, with Fresnel rims and correct glass-behind-glass compositing. Two overlapping spheres over a backdrop now look like two overlapping spheres over a backdrop.</description></item><item><title>The Mirror Test</title><link>https://uselux.app/devlog/the-mirror-test/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-mirror-test/</guid><description>Chrome finally mirrors its environment. The reflection blur schedule is now logarithmic and keyed to resolution, brushed metal streaks its reflections like actual brushed metal, and clearcoat reflects at its own polish instead of borrowing the base layer&amp;rsquo;s.</description></item><item><title>The Case of the Hoarded Gigabytes</title><link>https://uselux.app/devlog/the-case-of-the-hoarded-gigabytes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-case-of-the-hoarded-gigabytes/</guid><description>Found and fixed the leak that made every headless render permanently keep about 100MB: the bindless texture registry was immortalizing its texture views, and each one pinned an entire GPU device. Render farms, exports, and CI machines can breathe again.</description></item><item><title>The Helmet Was Fine, Actually</title><link>https://uselux.app/devlog/the-helmet-was-fine-actually/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-helmet-was-fine-actually/</guid><description>glTF assets now sample their textures the way the file says to: per-texture wrap and filter modes reach the GPU, so the community&amp;rsquo;s favourite battle-damaged helmet renders its full texture set instead of near-black streaks. Tiling textures tile, pixel-art textures stay crisp.</description></item><item><title>Standing Up Straight</title><link>https://uselux.app/devlog/standing-up-straight/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/standing-up-straight/</guid><description>glTF scene-node transforms now apply on import: assets arrive in the pose and place their file declares. The battle-damaged helmet finally faces the camera, visor HUD glowing, and multi-part scenes assemble instead of collapsing onto the origin.</description></item><item><title>The Walk Was in the File</title><link>https://uselux.app/devlog/the-walk-was-in-the-file/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-walk-was-in-the-file/</guid><description>Characters now play the animation their file ships: keyframed walks, idles, and dances drive the skeleton instead of a hardcoded placeholder wiggle. SkeletonPose becomes a real clip player with time, speed, and loop controls.</description></item><item><title>Lacquer, Velvet, and Brushed Steel</title><link>https://uselux.app/devlog/lacquer-velvet-and-brushed-steel/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/lacquer-velvet-and-brushed-steel/</guid><description>Imported glTF files now bring their whole material with them: clearcoat lacquer, velvet sheen, glass transmission, brushed-metal anisotropy, HDR emissives, and real refraction indices, textures included. If the artist authored it, Lux renders it.</description></item><item><title>The Weave Comes Into Focus</title><link>https://uselux.app/devlog/the-weave-comes-into-focus/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-weave-comes-into-focus/</guid><description>Imported textures now tile, rotate, and offset exactly as their author set them, and a second UV channel means occlusion maps stop stealing the wrong coordinates. The fine print from three releases ago is paid off.</description></item><item><title>The Streak That Wouldn't Turn</title><link>https://uselux.app/devlog/the-streak-that-wouldnt-turn/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-streak-that-wouldnt-turn/</guid><description>Brushed-metal and hair anisotropy now stretch along the surface&amp;rsquo;s own grain instead of a fixed screen direction. Rotate the lamp, and the streak turns with it; before this, it didn&amp;rsquo;t move at all.</description></item><item><title>Mind the Gaps</title><link>https://uselux.app/devlog/mind-the-gaps/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/mind-the-gaps/</guid><description>Curved meshes could develop see-through patches that wandered when the camera moved. The cluster culling maths was wrong in three separate ways, and hidden-surface skipping turned out to be off entirely. All four are fixed, and the chair is whole again.</description></item><item><title>A Cleaner Cut</title><link>https://uselux.app/devlog/a-cleaner-cut/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/a-cleaner-cut/</guid><description>Cone culling from Mind the Gaps was mathematically correct but ran on clusters shaped by whatever order triangles happened to load in. Lux now clusters meshes the way the industry actually does it, and the optimisation finally has something worth optimising.</description></item><item><title>Too Still to See</title><link>https://uselux.app/devlog/too-still-to-see/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/too-still-to-see/</guid><description>Depth of field, ambient occlusion, and any post-FX downstream of a still scene would quietly go black a few seconds after you stopped touching the patch. The engine mistook stillness for abandonment. It no longer does.</description></item><item><title>Framed Out</title><link>https://uselux.app/devlog/framed-out/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/framed-out/</guid><description>Point a camera at the corner of an instanced grid, away from where it&amp;rsquo;s anchored, and the whole thing would vanish. Ten thousand cubes, gone, because the culler was asking about one cube that wasn&amp;rsquo;t on screen. It stays on screen now.</description></item><item><title>A Look That Loads</title><link>https://uselux.app/devlog/a-look-that-loads/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/a-look-that-loads/</guid><description>Drop a .cube LUT onto your footage to grade it and the whole app used to fall over. A colorist&amp;rsquo;s entire look library, every one a trap door to the desktop. Now the look loads and the grade lands.</description></item><item><title>The Fountain That Stayed Up</title><link>https://uselux.app/devlog/the-fountain-that-stayed-up/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-fountain-that-stayed-up/</guid><description>Push a 2D particle fountain hard enough and the whole frame used to blink out to a flat blank. No error, no warning, just gone. Now the fountain keeps spraying and tells you when you&amp;rsquo;ve asked for more than it can draw.</description></item><item><title>The Filters That Stopped Eating Light</title><link>https://uselux.app/devlog/the-filters-that-stopped-eating-light/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-filters-that-stopped-eating-light/</guid><description>Drop any 2D filter onto a 3D render and the picture used to sink into the dark, one filter darker, then two. A blur set to zero, a soft glow at neutral, an occlusion pass turned all the way down: none of them should touch a single pixel, and all of them did. Now a neutral filter leaves the frame exactly as it found it.</description></item><item><title>The Neutral That Wasn't</title><link>https://uselux.app/devlog/the-neutral-that-wasnt/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-neutral-that-wasnt/</guid><description>WhiteBalance at its default now leaves your image untouched, instead of quietly tinting it green. And FXAA finally admits what it actually is.</description></item><item><title>Bloom, On Time</title><link>https://uselux.app/devlog/bloom-on-time/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/bloom-on-time/</guid><description>Bloom was arriving a frame late on real graphics cards, and on the standalone node it never arrived at all. Now the glow lands on the frame that earned it.</description></item><item><title>Nodes That Did Nothing</title><link>https://uselux.app/devlog/nodes-that-did-nothing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/nodes-that-did-nothing/</guid><description>Eighteen nodes in the browser produced no pixels, no matter how you wired them. They are gone. Three more that cannot draw yet now say so out loud, in red, instead of failing politely. The Shadertoy node, which had quietly stopped compiling anything at all, works again.</description></item><item><title>The Site Gets a Stage</title><link>https://uselux.app/devlog/the-site-gets-a-stage/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-site-gets-a-stage/</guid><description>uselux.app gets a full redesign: stage black, one luminous gold, a serif that means it. Features and roadmap pages join the devlog, and every number on the homepage is computed from content, not promised.</description></item><item><title>When Transparent Means Transparent</title><link>https://uselux.app/devlog/transparent-means-transparent/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/transparent-means-transparent/</guid><description>The 2D Blend and Composite nodes now follow the actual compositing spec, so a transparent layer stays out of the way, a Multiply looks like a Multiply, and half-opacity means half, not all. Group and Merge get a blend mode pin, and a blended group finally minds its own business.</description></item><item><title>No More Banding</title><link>https://uselux.app/devlog/no-more-banding/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/no-more-banding/</guid><description>The 2D filter chain now carries floating-point precision from node to node instead of rounding to 8 bits at every hop, so deep filter stacks stop banding and highlights above white survive the trip. Photos and 3D renders get the same treatment, which is a polite way of saying they previously did not. And Export Image now saves the frame you are actually looking at.</description></item><item><title>Nodes Show Their Work</title><link>https://uselux.app/devlog/nodes-show-their-work/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/nodes-show-their-work/</guid><description>Every node now wears its live output on its body. Numbers read out as they change, colours show the colour, shape nodes draw the shape they are emitting, and texture nodes preview their own pixels. The patch stops being a diagram of what should be happening and starts being a readout of what is.</description></item><item><title>GO Moves the Pixels</title><link>https://uselux.app/devlog/go-moves-the-pixels/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/go-moves-the-pixels/</guid><description>Cues now do something. Press GO and the output crossfades from one look to the next over the fade time you set, on the curve you chose. Capture the look you have, name it a cue, and it becomes a thing you can hit on the night.</description></item><item><title>Subpatches Remember</title><link>https://uselux.app/devlog/subpatches-remember/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/subpatches-remember/</guid><description>Group nodes with Ctrl+G and they keep what they were holding. Save, reopen, and the group is still there. Ctrl+Z takes the grouping back, Ctrl+Shift+G opens the box. Until now the inner graph quietly evaporated on reload, taking every node inside it with it.</description></item><item><title>The Banner That Hands Your Set Back</title><link>https://uselux.app/devlog/the-banner-that-hands-your-set-back/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-banner-that-hands-your-set-back/</guid><description>Lux has been quietly writing a snapshot of your patch every time it dies. Now the next launch actually tells you, with a Restore button, instead of leaving the file in a cache folder for you to find on your own.</description></item><item><title>The Editor Learns To Sit Still</title><link>https://uselux.app/devlog/the-editor-learns-to-sit-still/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-editor-learns-to-sit-still/</guid><description>Leave Lux open on a patch that is not moving and it will now stop drawing it, instead of repainting the same picture 120 times a second until your battery gives up. The canvas grid stopped multiplying too: zoom all the way out and it spreads its dots instead of packing 203,000 of them into a frame you could not see anyway.</description></item><item><title>Pick a Screen, Any Screen</title><link>https://uselux.app/devlog/pick-a-screen-any-screen/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/pick-a-screen-any-screen/</guid><description>Output now goes fullscreen on the display you choose, not the one it happens to be sitting on. Pick the projector once in Project Settings and Lux remembers it, so tomorrow night the same rig comes up pointing at the same screen.</description></item><item><title>The Diff That Was Not There</title><link>https://uselux.app/devlog/the-diff-that-was-not-there/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-diff-that-was-not-there/</guid><description>Open a patch, change nothing, save it, and the file stops moving under you. Saves are byte-stable, so version control shows the one value you moved instead of a few hundred lines of reshuffled noise. And an open-and-save no longer quietly deletes the title, author and description you wrote.</description></item><item><title>The Node Your Laptop Cannot Run</title><link>https://uselux.app/devlog/the-node-your-laptop-cannot-run/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-node-your-laptop-cannot-run/</guid><description>Open a patch on a machine that is missing one of its plugins, and the nodes it cannot run are now held, drawn, and written back out exactly as you left them. Ctrl+S on the wrong laptop used to quietly delete them.</description></item><item><title>The Patch That Did Not Arrive Intact</title><link>https://uselux.app/devlog/the-patch-that-did-not-arrive-intact/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-patch-that-did-not-arrive-intact/</guid><description>Lux always knew when a file did not open the way you wrote it. It just told the log instead of telling you. Now a strip across the top of the editor names the file, counts what went missing, and hands you the list.</description></item><item><title>Watch It Animate, It Said</title><link>https://uselux.app/devlog/watch-it-animate-it-said/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/watch-it-animate-it-said/</guid><description>The welcome card promised an animated star. It rendered a frozen one, in the corner, forever. Every patch in Lux was checked on the way in except the ones a new artist actually opens. Now they are checked too, and they do what their cards say.</description></item><item><title>The Node That Knew It Was Broken</title><link>https://uselux.app/devlog/the-node-that-knew-it-was-broken/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/the-node-that-knew-it-was-broken/</guid><description>A shader that will not compile, a glTF file that is not there, a pipeline the driver refuses: all of it used to happen in a terminal you were not reading, while the patch just went quiet. Now the node that failed turns red and tells you why.</description></item><item><title>Which Build Is This?</title><link>https://uselux.app/devlog/which-build-is-this/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://uselux.app/devlog/which-build-is-this/</guid><description>Every Lux binary now says exactly which commit it came from. Version numbers only change when a release ships, so &amp;lsquo;v0.44.0&amp;rsquo; was previously true of the release, of my breakfast-time build, and of yours. Now it isn&amp;rsquo;t.</description></item></channel></rss>