Notes for future brainstorming. Nothing here is decided or scheduled.
Robert's list, in his order. Two of them turned out to have history in the repo already — noted against each so nobody starts from scratch.
-
A page that credits every tool and data source used. Several of them require it: Open-Meteo asks for attribution under CC-BY 4.0, the tile providers require their own credit, and the fonts carry SIL OFL notices. Today the map's ⓘ names only whichever basemap and weather layer is switched on, and the font licences sit unread in
public/fonts/. This needs to be one page listing all of it — libraries, data, tiles, fonts — reachable from the menu, not assembled per screen. -
PWA — audit rather than build. It already is one:
manifest.json, a 192 and a 512 icon, an apple-touch-icon and a registered service worker are all in place, and the worker precaches the fonts and caches map tiles. So the real work is checking what is weak: installability on iOS, whether the app shell survives a cold start with no network, whether the update toast is reachable, and what happens to a wind field with no connection. Run Lighthouse's PWA audit and fix what it names, rather than rebuilding what exists. -
A start page showing the tools as cards.
/currently redirects straight to the first entry inapp/data/tools.ts, which was deliberate — it stopped any one tool being the accidental default — but it also means a visitor never sees that there is more than one. Cards would carry thenoteandaboutcopy each tool already has intools.ts. Watch the routing rule in AGENTS.md: every tool keeps its own URL, and the start page must not become a second source of truth about which tools exist. -
"Will there be shadow when we want to be there?" — the next mini app, and the most important one. A 3D view that takes the sun's real position for the chosen place and time and casts the shadows that follow from it.
*This has a research section already*, further down under [Tools that fit the LightKit suite](#tools-that-fit-the-lightkit-suite) → **Sun direction and shadow calculator**, including a comparison of the 3D globe libraries that can render sun shadows and whether they shadow terrain. The open question recorded there — whether a whole globe is needed at all, or just a small local scene in plain three.js — is the first thing to settle, because it decides the weight of the whole app. The unanswered sub-question is where terrain height comes from, and whether flat ground plus object height is enough for what pilots actually ask. -
Cookie settings. Note that the app currently sets no cookies at all — everything it remembers lives in
localStorageunder thedpt:namespace, pluscolor-themeand the OpenWeatherMap key. That is still a consent question in the EU, since the rules cover storage on a device rather than cookies specifically. So this is really two decisions: what the app must ask permission for, and whether anything here counts as strictly necessary. Worth answering before writing a banner nobody needs. -
Split the menu: a new "Infos" group, and rethink Settings. The "More" group has become a bag holding two different kinds of thing — Light phases and About ND filters explain, while Settings configures. Those should be separate groups. The settings drawer wants the same look: it now holds an API key, weather overlay strengths, a pre-flight checklist and system utilities, and the checklist in particular is reference material rather than a setting.
-
Decide what “today” means for the weather report. The date-only selector is converted to noon. Late in the day, that target is already outside OpenWeather's remaining forecast blocks, so the closest available block can be early tomorrow while the selected date still says today. The new
Forecast 02:00source-time row makes the mismatch visible. The product decision is whether today means current conditions or a noon planning forecast; air quality must then follow the same time instead of always remaining current. -
Use the local calendar date when the solar planner opens. It currently takes
new Date().toISOString()and therefore uses the UTC date. In a positive UTC offset, the app opens on yesterday between local midnight and UTC midnight. This was observed in Vienna immediately after midnight on 2026-08-23. -
Confirm the mouse-wheel zoom fix. Robert reported that after closing the wind reading, wheel zoom stopped responding. It could not be reproduced with synthetic events in either mode. The likely mechanism is the native context menu interrupting the pointer sequence, so the
pointerupOpenLayers waits for never arrives and its view is left with an interaction open — and the context menu is now suppressed over that layer, which should remove the cause. Unverified on real hardware. If it recurs, instrument OpenLayers' view interaction counter rather than guessing again. -
Open-Meteo is free for non-commercial use only. If LightKit ever becomes commercial, the wind field needs a paid plan. Its quota is also charged per location, not per request — a fact found the hard way via a 429 — so the 8×8 grid, the movement threshold and the ten-minute cache in
useWindField.tsare load-bearing, not tidiness. -
Some labels are at 10px, one under the 11px floor AGENTS.md sets for Josefin's small x-height, and the basemap buttons are at 9px. Both were asked for explicitly. Worth a look on a real phone in daylight before it becomes the house style by accident.
-
The wind field's slowest cells still barely move. The field is scaled so its fastest cell is readable, and the ratios are exact, so a cell at 0.4 km/h beside one at 37 km/h is correctly almost still. That is honest, but it may read as "broken" to someone who has not been told. A one-line legend or a note in the dialog might be worth it.
-
The under-one-hour case in "next window" still shows only a start time. Every other path now shows a start and an end. It was left alone because "starts 19:24" followed by a range contradicts itself, so it needs its own small decision.
-
Maybe a whole 3D library for a globe is not necessary. It could be enough to just render the current location in 3D and with shadows.
If the view never leaves the selected spot, there is no globe to navigate — only a small local scene. That could be plain three.js with a directional light set from SunCalc's azimuth and altitude, at a fraction of Cesium's weight, and it could work offline. Open sub-questions: where would the terrain height data come from for a local scene, and is flat ground plus object height enough for the cases pilots actually care about? See the shadow research below.
Split out of the dial redesign,
docs/superpowers/specs/2026-08-16-nd-dial-redesign-design.md.
Held back to keep that change to one screen's worth of work.
The reference screenshots carried a second knob labelled "AMBIENT CONDITIONS (EV)", running from snow and sand down to sunset. It is real photography — the sunny-16 table — and it answers a question the tool cannot answer today: what do I fit before I launch, when there is no metered reading to type in?
- A named-condition to EV100 table: snow/sand 16, direct midday sun 15, hazy sun 14, cloudy bright 13, overcast 12, golden hour 11, sunset 9.
-
shutterFromEv(ev, fNumber, iso), which ist = (N² × 100) / (2^EV × ISO). Pure, unit-testable, no Vue. - An ISO input, because the sum above needs one and the metered path does not.
- A second
FrameRateDial-shaped control for the conditions.
The open design problem, and why it was deferred rather than just built. This is a second, competing answer to "how bright is it?". With both on one screen a pilot cannot tell which input is driving the result. It needs an explicit mode switch — "I metered it" against "guess from the sky" — and that switch is the part that wants its own brainstorm.
Note that this does not breach product rule 1. Aperture and ISO would be readings the user types, exactly like the metered shutter speed. The camera model still never enters a calculation.
Also split out of the dial redesign.
The redesign adds an aperture slider and an "adjust aperture to f/x" readout, but knows nothing about what any given aircraft can actually do. Two gaps follow:
- Many drones have a fixed iris — a DJI Mini 4 Pro is locked at f/1.7 and cannot be stopped down at all. The tool cannot warn those pilots that the readout is useless to them. Today's mitigation is that the slider defaults to an "off" position, so nobody is handed advice unasked.
- Aircraft with a variable iris still have limits (Mavic 4 Pro main is f/2.0–8, Mavic 3 Pro main f/2.8–11). A suggestion outside that range is currently only clamped to the f/1.4–22 series, not to the aircraft.
Filling this means an aperture field on GearEntry for roughly fifty models,
each with an evidence URL and a checked date, per product rule 2. It is a
research job, not a coding one.
Whatever it holds is presentation and warnings only. Product rule 1 stands: picking a camera must still change no number.
Designed and planned on 2026-08-16. Only Task 1 was built (Vitest plus the
pure app/utils/nd.ts module). Tasks 2 to 6 are written out in full and are
waiting:
- Task 2 — the
app/data/gear.tsdata layer, with schema tests - Task 3 — research every DJI and non-DJI model and fill that file
- Task 4 — the gear picker, the facts strip, the filtered ND grid
- Task 5 — the in-app "model not listed" list of unverified models
- Task 6 — the mobile-first reorder, so the result sits above the grid
Full steps, code and reasoning:
docs/superpowers/plans/2026-08-16-nd-gear-presets.md
Task 3 is the reason the rest is held back. It is a long research grind across
many makers' product pages, and it does not belong in the same sitting as
anything else. The rule agreed for it: a model whose ND set cannot be
confirmed against a real product page is never guessed. It goes in an
unverifiedGear list with a reason and a check date, and the app shows that
list, so a gap stays visible.
- Needs a brainstorm before it can be planned.
The ND tool asks the user to type in the shutter speed their camera meters without a filter. That works on site, with the aircraft already up, and the answer is exact.
It does not work at home the night before, when the user is deciding which filters to pack. LightKit plans golden hour, so packing is exactly when it gets opened.
The fix is a second way to fill the same field: pick the conditions — bright sun, hazy sun, cloudy, golden hour — and let the app estimate. Everything downstream is unchanged.
The blocker is that an estimate needs aperture and ISO, which a measured reading already contains. The Mavic 3 Pro's main camera covers f/2.8 to f/11, four stops that change the answer completely. The open question is how many inputs a user will accept before walking outside and reading the screen is less effort than answering the app.
Written up in
docs/superpowers/plans/2026-08-16-nd-required-strength.md,
"Task 5 is not written yet".
Retracted 2026-08-16, same day it was written.
The original claim was that the header overflowed by 8px and made a phone
scroll sideways. It does not. The measurement was bad: it read a container's
scrollWidth while the page width had been forced with inline styles, which
also caught the full-width background canvas.
The real test is documentElement.scrollWidth > documentElement.clientWidth,
and it is false. The 8px is the icon button's deliberate -m-2 negative
margin, which lets the icon's visual edge line up with the text edge instead of
its padding box. It bleeds into the 16px safe-area padding, never reaches the
viewport edge, and the page container is overflow-x: hidden regardless.
Kept as a note because "measure the page, not a div" is the lesson, and because anyone reading the old entry would go hunting for a bug that was never there.
- Small, high value.
test/gear.spec.ts checks the shape of app/data/gear.ts — id format,
non-empty sources, ISO dates. It cannot catch a value that is well-formed but
wrong: a mistyped URL, a stop value off by one, a stale date.
Both times this data was reviewed, the reviewer had to write a throwaway script
to diff the module against _MATERIAL/common-nd-gear-seed.json field by field.
That script should be a test. Then the seed becomes the permanent fixture and
any drift fails CI forever, instead of depending on someone thinking to check.
ND tool: the recommended filter can be hidden by the gear narrowing
- UX question, not a bug.
The answer card recommends from the full ND range, because the product rules forbid snapping to a commercial kit. The outcomes table below narrows to the filters sold for the selected aircraft.
So a combination exists where the card says "FIT ND4" while the narrowed table shows only ND8 upward — the recommendation is not among the visible rows until "show all" is tapped. Both behaviours are individually correct and required. Together they read oddly.
Options worth weighing: always keep the recommended row visible even when narrowed; or say plainly in the table when the recommendation sits outside the selected aircraft's set. Decide before this confuses a real pilot.
Found during the review on 2026-08-16. Deliberately left alone so far.
- No aperture, no ISO. The Mavic 3 Pro's main camera covers f/2.8 to f/11, about four stops the tool ignores. Photographers cannot be served without these.
- Aperture as an alternative to ND. The Mavic 3 Pro's main camera covers f/2.8 to f/11, about four stops. "Stop down or fit a filter?" is a real field question, but it is a comparison tool, not an input to this one.
- Variable ND filters. Sold as a range, such as ND2-32. The current model holds one strength per filter and cannot express a range.
The ND tool's gear presets cover drones only. The same preset shape should extend to the rest of the field kit:
- Action cameras — GoPro, Insta360, DJI Osmo Action. Mostly fixed aperture, so ND is the only exposure control besides ISO. A close fit for the drone shape.
- 360 cameras — DJI Osmo 360, Insta360 X5. Two lenses, so a filter set is a
pair. The
ndStopsshape may need a note field for that. - Mirrorless and DSLR — filters are sized by thread diameter (49mm, 67mm, 82mm), not by camera. A preset would carry a lens's thread size, not a body's. This needs its own design pass; it does not fit the drone shape.
- Phones — clip-on and case-mounted filter systems exist (Moment, Freewell). Worth checking whether anyone actually uses them.
The open question: does one GearPreset type stretch over all four, or does the
interchangeable-lens case need a separate model? Decide before adding the second
category.
LightKit is a solid choice. Keep it as one word. It clearly covers natural light, optical filters, and exposure tools.
- The name is short, memorable, and easy to type.
- It works for web addresses (e.g.
lightkit.apporlightkit.dev). - It does not lock you into drones only, so traditional photographers and videographers will use it.
- Calculates the sun angle (azimuth) and shadow length for any location and time.
- Helps drone pilots avoid direct lens flare and helps photographers find shaded ground.
Checked August 2026.
| Library | Shadows from sun | Terrain casts shadow | Notes |
|---|---|---|---|
| CesiumJS | Yes | Yes | The reference implementation. Apache 2.0. |
| ArcGIS Maps SDK for JS | Yes | No | Polished, ready-made Daylight widget. Esri licence. |
| deck.gl | Yes, on flat maps | n/a | GlobeView is experimental and documents no shadow support; TerrainLayer unsupported there. |
| MapLibre GL JS v5 | No built-in | n/a | Has globe projection, 3D terrain, building extrusions. No sun-driven shadow casting. |
| Shadowmap.org | Yes | Yes | A finished commercial product, not a library. Worth a look before building. |
CesiumJS is the only one that shadows terrain, which is the case that matters for a pilot in a valley:
viewer.shadows = true
viewer.scene.globe.enableLighting = true
viewer.clock.currentTime = /* scrub time, shadows move */Sun position comes from Simon1994PlanetaryPositions. viewer.shadowMap tunes
darkness, max distance and bias. Cesium OSM Buildings or Google Photorealistic
3D Tiles give real building shadows.
ArcGIS is easier to stand up but shadows only real-world 3D objects — terrain does not cast, which rules out the mountain-shadow case.
Cesium is a multi-megabyte engine plus workers, with terrain and imagery streamed from a service. The whole app is currently 620 KB gzipped and works offline with cached tiles and self-hosted fonts. Adding Cesium to the main bundle would give that up.
Tier 1 — shadow vector on the existing 2D map. No new dependency. SunCalc already returns azimuth and altitude; the rest is trigonometry:
shadowLength = objectHeight / tan(sunAltitude)
shadowDirection = sunAzimuth + 180°
Drawn as an arrow or polygon on the OpenLayers map. Works offline, costs nothing.
Tier 2 — optional 3D view, Cesium, lazy-loaded on its own route, online only. Never part of the main bundle. Only worth it for real terrain and building shadows.
The deciding question: does a pilot need to see a mountain's shadow fall across a valley, or just know the sun is at 240° and 12° up? If the second, Tier 1 is the whole feature.
Sources: CesiumJS ShadowMap · Cesium community: shadow and sun analysis · deck.gl GlobeView · ArcGIS scene shadow sample · ArcGIS Daylight widget · Shadowmap
- Calculates the required interval between shots based on event duration, frame rate, and final clip length.
- Prevents wasted storage and battery during automated shoots.
- Calculates new shutter speeds when you stack heavy ND filters (such as ND64 or ND1000).
- Provides an on-screen countdown timer for manual bulb-mode exposures.
- Calculates the closest focus distance that keeps the background acceptably sharp.
- Helps landscape photographers set manual focus quickly without guessing.
- Displays moonrise, moonset, illumination percentage, and astronomical twilight.
- Helps creators plan night sky, Milky Way, and low-light shoots.