PSFCraft WebUI

Interactive, browser-based PSF (Point Spread Function) visualisation tool. Distributed as a single static HTML file — no build step, no package manager, no external CDN dependencies. Works on any modern browser, including file:// origins.


Live Demo

Try the WebUI directly in your browser — no installation required:

Fullscreen

Use the dedicated controls inside the WebUI to maximise the canvas. If the iframe is too small, you can also open the standalone app ↗ in a new tab.


Quick start

# Open directly in the browser — no server required
xdg-open psfcraft-webui.html        # Linux
open psfcraft-webui.html            # macOS
start psfcraft-webui.html           # Windows

Or serve locally (avoids file:// cross-origin quirks in strict browser configs):

python -m http.server 8080          # then visit http://localhost:8080/psfcraft-webui.html

Features

Feature Description
Real-time PSF Diffraction PSF recomputed on every slider change via in-browser FFT
Zernike controls Defocus, Astigmatism 0°/45°, Coma X/Y, Trefoil, Spherical (OSA/ANSI indexing)
Aperture types Newtonian (3-spider), Newtonian (4-spider), Circular (clear), Annular
View modes PSF intensity / Aperture mask / Wavefront Error (WFE) map
Split mode Two independent optical systems rendered side by side on one canvas
Decomp mode One PSF tile per Zernike term (isolated contribution); shared colour scale
Colormaps Inferno, Viridis, Hot, Gray, Coolwarm
Intensity scale Log (default) or linear; adjustable min/max display range
Pixel binning Simulates detector undersampling (1 × – 64 ×)
Detector noise Poisson shot + Gaussian read noise with flux and read-noise sliders

Architecture

The file is self-contained (< 1 300 lines). The JavaScript is structured into twelve named sections separated by banner comments:

§ Name Exports
1 2-D FFT fft1d, fft2d, fftShift
2 Pupil mask buildPupil
3 Zernike polynomials zernikeValue, buildWFE
4 PSF computation computePSF, binPSF, applyNoise
5 Colormaps & colour utilities CMAPS, lerp, applyColormap, wfeColor, renderDivergingColorbar
6 Canvas renderers renderAperture, fillWFEPixels, renderWFE, renderPSF
7 PSF metrics measureFWHM, rmsWFE
8 Application state state
9 PSF cache psfCache, psfCacheKey, getCachedPSF
10 Render scheduler & doRender scheduleRender, doRender, renderSingleMode, renderSplitMode
11 Decomposition mode DECOMP_TERMS, renderDecomp
12 UI wiring & boot setMode, setView, wireSlider, DOMContentLoaded

How PSF computation works

buildPupil(N, aperture, obscuration)
    → Float32Array[N×N]  amplitude mask  P(x,y)  ∈ {0, 1}

buildWFE(N, coefficients)
    → Float64Array[N×N]  wavefront error  W(x,y)  [λ units]

Complex pupil  U = P · exp(2πi · W)

fftShift → fft2d → fftShift          (Fraunhofer diffraction)

PSF = |U|²,  normalised so  Σ PSF = 1

binPSF(PSF, N, b)                    (optional detector binning)
applyNoise(PSF, flux, readNoise)     (optional Poisson + Gaussian noise)

The PSF is computed at full resolution N and cached. Binning and noise are applied after retrieval from the cache, so changing the sampling factor or noise level never invalidates the expensive FFT step.


Performance notes

Grid size Typical render time
128 × 128 < 10 ms
256 × 256 20–60 ms
512 × 512 150–500 ms

Times measured on a mid-range desktop CPU in Chrome 124. The PSF cache (getCachedPSF) memoises up to 40 unique (aperture, obscuration, coefficients, N) combinations; subsequent renders for the same configuration are free.


Development notes

  • No build step. Edit psfcraft-webui.html directly.
  • Indentation: 2 spaces (matching the rest of the project's JS conventions).
  • Style: ES2020+, "use strict", const/let only, JSDoc on public functions.
  • Testing: Open the file in a browser and exercise all controls. Automated headless tests are not yet implemented.

Adding a new Zernike term

  1. Add a zernikeValue case in § 3.
  2. Add a slider row in the HTML <aside id="sidebar"> and in <aside id="panel-ctrl-R">.
  3. Register it in the aberDefsL and aberDefsR arrays in § 12.
  4. Add the term to DECOMP_TERMS in § 11 if it should appear in Decomp mode.

Deployment (GitLab Pages)

The file is automatically copied to public/webui/index.html during the documentation build. After a successful push to main the WebUI is accessible at:

https://<namespace>.pages.gitlab.io/<project>/webui/

The copy step is handled in Makefile (pre-build target):

mkdir -p public/webui
cp webui/psfcraft-webui.html public/webui/index.html