Caustica docs 12. Code structure Edit on GitHub

12. Code structure

Caustica is written in vanilla JavaScript with no framework. The source lives in /assets/caustica/.

lens.js

Pure physics; no DOM access or rendering.

renderer.js

WebGL2 GPU renderer.

main.js

Application shell (~6400 lines).

style.css

Single stylesheet using CSS custom properties for theming (--accent, --lens-color, --src-color, --hybrid-color, and others) with html[data-theme="dark"] overrides. The layout is a CSS grid. From 641px up its areas are the stage (which letterboxes the square image using container-query units) over the scene drawer on the left, then the rail, then the category switcher on the far right; the timeline, the rail’s tab bar and the plane card are hidden. In the stage, .sl-panes lays out one or two .sl-panes, each a tab strip over an image wrapper sized from container-query units (half the width each when .sl-split is set). The tabs are folder tabs: a pane’s frame colour and width (--fc, --fw; teal only while a tab is dragged onto the pane) outline both its image and its showing tab, and the focused pane’s showing tab carries a teal line along its top (.sl-pane-focus), which overhangs the frame’s top edge by the border width so the two read as one shape. The frame is drawn by an ::after overlay above the canvas rather than as the wrapper’s border, since a canvas clipped to a border’s inner curve eats into the line at the rounded corners. The placement tool column has a hairline on its right. The link button and its menu are placed halfway down the gap between the panes with the same container-query arithmetic; on phones the pane wrappers are display: contents, so the image wrapper lays out as before, and the tab strips, the second pane and the drop zone are hidden. The drawer’s cards keep a fixed size (the selected one wider, with its tool and side columns) and scroll sideways when they outgrow the drawer. Narrow screens (≤ 640px) collapse to a single scrolling column: image, then the rail, with a mobile-only Object tab hosting the object controls and the plane viewer moving into the Scene tab above the redshift timeline. There the viewer’s tools become a single column of larger tap targets (L / S / H, and M in the delensed view, at the top, a trash button floated to the bottom), the ‹ › arrows hug the canvas, and the tool column and the Redshift / Plane column stretch to the canvas height and sit an even gap from it, so the four control clusters frame the canvas at its corners. Short landscape viewports (height ≤ 480px, e.g. a phone held sideways) show a full-screen prompt asking for a taller window instead of the app.

gif.js + gif.worker.js

A vendored local copy of the gif.js library. gif.worker.js carries one local patch: findClosestRGB memoises its lookups, since with the shared palette from _renderGif() in main.js it would otherwise scan all 256 entries for every pixel. Loaded lazily (only when GIF recording is requested) via a dynamic <script> tag, avoiding cross-origin Web Worker restrictions that would arise from a CDN-hosted copy.