A static, browser-only LCARS-themed calculator for the blackbody photon flux landing on a detector pixel through a three-stage cryogenic filter stack. A 300 K source radiates through three cold stages (default 65 K / 4 K / 800 mK) onto the pixel; the tool plots counts/nm vs wavelength and reports the integrated counts/sec.
- Reactive optical-bench schematic with the controls laid out per stage (temperature, aperture diameter, axial distance, substrate + thickness, coating).
- Full radiative model: the 300 K source and each cold stage emit as graybodies, each attenuated by the colder downstream filters; cold-stop geometry sets the étendue onto the pixel.
- Thickness-scaled substrate materials computed from first principles: N-BK7, fused silica (Suprasil / Infrasil), sapphire, MgF₂, CaF₂.
- Coatings from a measured-curve library (DARKNESS, PICTURE-C, ASAHI YSC1100, ASAHI YSC0750, ITO, M254C cold mirror), plus CSV import and a Custom coating (ideal top-hat or a designed Ta₂O₅/SiO₂ multilayer).
- Coating designer: synthesize a real Ta₂O₅/SiO₂ stack from a passband target (transfer-matrix forward model + bounded refinement), edit the layer list by hand, and see the designed filter's throughput overlaid on the flux plot on a log right-hand axis.
The app must be served over HTTP — ES-module imports and fetch() of the filter JSON do not
work from file://.
python3 -m http.server 8000 # or: npm run serve
# open http://localhost:8000/npm test # node --test over test/*.test.jsPhoton spectral radiance L_ph(λ,T) = (2c/λ⁴)/(exp(hc/λk_BT) − 1); each emitter k contributes
A_pix · QE · Ω_k · ε_k(λ) · (∏ downstream T_j) · L_ph(λ,T_k), where Ω_k is the projected solid
angle of the limiting (cold-stop) aperture between plane k and the pixel. The per-nm spectrum is
integrated (trapezoid) to counts/sec. Units and assumptions are documented inline in js/physics.js.
Coatings and substrates share one swappable OpticalElement interface (transmission/reflection
over a wavelength grid), so the flux engine never cares how an element's curve was produced:
- Substrates (
js/materials.js) are computed from physical thickness:T = (1−R)²·τ(λ,d),ε = (1−R)(1−τ), withRfrom the Sellmeier refractive index and bulkτfrom either a tabulated absorption coefficient (fused silica, sapphire, MgF₂, CaF₂) or the measured N-BK7 curve scaled from its 10 mm reference (Beer–Lambert). Each stage has a Thickness (mm) input. - Coatings are measured
T(λ)curves (js/filters.js), the analytic Custom top-hat (CustomCoating), or a designed multilayer (MultilayerCoating). - Multilayer designer (
js/tmm.js,js/synthesis.js): a transfer-matrix forward model for Ta₂O₅/SiO₂ stacks, plus a synthesizer that builds a chirped long-wave-blocking edge filter from the Custom passband and refines the layer thicknesses (asymmetric objective: maximize in-band, suppress long-wave leakage out to the ~3.4 µm glass cutoff, leave the short side free). Layer count is adjustable; the result is demonstration-grade (seejs/synthesis.jsfor the limits).
The bundled coating curves in data/filters/*.json are checked in and are all the app needs. They
were converted from raw manufacturer/measured source curves (kept outside this repo) with the py313
- numpy scripts in
tools/;tools/notebook_parity_reference.pyregenerates the physics parity fixture from the bundled curves. Substrate material data (Sellmeier coefficients + absorption tables, with citations) lives inline injs/materials.js. You can also import your own coating/substrate curve at runtime from a 2-column file (fraction / percent / OD).
Settings → Pages → Deploy from branch → main, / (root). All files are static at the repo root;
no build step.