Abyssal Station 12 ← return to the water

Field notes / build 18

How to make darkness do the talking.

Abyssal Station 12 is an observational research post at 3,800 metres. The site borrows its etiquette from that depth: don’t flood the scene with light, and don’t assume the first flicker is yours to keep.

01 / concept

A page that asks the visitor to look slowly.

The usual landing page makes itself available all at once. This one begins nearly black. Its text and organisms surface around a moving pool of light, so reading the station’s log feels closer to leaning over a viewport than scanning a brochure.

The visual language stays spare: a low-opacity humanist sans, thin rules, and data that has the modest scale of a late-night watch. Cyan signals living light; violet is reserved for stranger sightings and the instrument geometry.

02 / signature technique

A real field, not a dark gradient.

A fixed Canvas 2D layer maintains hundreds of tiny drifting organisms and a handful of siphonophore colonies. On every frame, each organism’s brightness is calculated from its actual distance to the pointer. The colonies are rebuilt as moving point chains and joined with quadratic Bézier curves, so the forms keep soft, unrepeatable motion rather than travelling a fixed SVG path.

The same pointer coordinates drive the document mask. A second, on-demand Canvas 2D scope below the instruments turns the visitor into a patient observer: the photon-counter dial looks for two narrow emissions, rather than rewarding every wavelength with the same result.

index.html / distance falloff + Bézier chain
const distanceLight = (x, y, radius = 370) => {
  const d = Math.hypot(x - state.x, y - state.y);
  return Math.max(0, 1 - d / radius);
};

const l = distanceLight(p.x, p.y);
if (!l) return;
const color = p.violet ? '138,92,240' : '63,224,208';
ctx.fillStyle = `rgba(${color}, ${Math.pow(l, 1.85) * pulse})`;

ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length - 1; i++) {
  const midX = (points[i].x + points[i + 1].x) / 2;
  const midY = (points[i].y + points[i + 1].y) / 2;
  ctx.quadraticCurveTo(points[i].x, points[i].y, midX, midY);
}
ctx.stroke();
index.html / photon sweep detection
const returns = [
  { center: 470, span: 11, label: 'copepod return acquired' },
  { center: 495, span: 10, label: 'siphonophore return acquired' }
];

const signalAt = (wavelength) =>
  returns.find((signal) => Math.abs(wavelength - signal.center) <= signal.span);

readout.textContent = signal
  ? `${wavelength} nm · ${signal.label}`
  : `${wavelength} nm · water quiet · no usable return in this band`;

03 / palette + type

Cold light, seen sparingly.

The page uses Instrument Sans, with a 300 weight for its quiet, open letterforms. The display scale is deliberately tight; it should feel like a sentence discovered, not announced.

Abyss#02040A
Bio-cyan#3FE0D0
Bio-violet#8A5CF0
Faint bone#D9E2E8

The field responds before the station speaks.Instrument Sans · 300 / 400 / 500

04 / iteration log

Three passes below the surface.

Pass 1 — correctness

  • Canvas safetyAdded a safe no-canvas fallback that removes the document mask instead of throwing and leaving the record unreadable. The device-pixel ratio remains capped at 2.
  • First paintSeeded one cyan siphonophore colony beside the initial light position, so the Bézier-chain technique is visible immediately instead of depending entirely on random placement.
  • Animation life cycleCentralised animation start/stop logic. The requestAnimationFrame loop now stays off for reduced motion and hidden documents, and pauses when the station scene is no longer intersecting the viewport.
  • Reading + controlsMade the document landmarks explicit, added keyboard skip links and 44px link targets, strengthened focus rings, and corrected the low-contrast violet swatch text. The cursor mask now keeps readable text fully opaque inside its reveal, and the guide’s footer link returns to the station rather than linking to itself.

Pass 2 — design depth

  • Repeated grammarThe transmission and logbook were both conventional split columns, which made the record feel assembled from one template. The signal now has an oversized, drifting timestamp behind its copy; the final log is an overlapping marginal note and a large indented quotation instead of another paired grid.
  • Flat watchThree equal observation cards gave every sighting the same weight. The watch is now an uneven field: a tall cyan-tinged squid record anchors the left while two horizontal reports stack on the right, with restrained oversized indices giving the timecodes a second scale.
  • Underplayed organismsThe Bézier colonies previously read as a single elegant line. Each visible colony now grows a few gently moving tendrils from its nodes, so the cursor light exposes a more convincing living structure rather than just a diagram of one.

Pass 3 — complexify

  • Second encounterBuilt the photon-counter sweep beneath the instrument list instead of adding another decorative card. Its canvas plots a noisy baseline and two narrow, colour-specific returns; the visitor can tune it with a real range input, touch, or keyboard arrows. The small Bézier specimen appears only inside a recorded band.
  • Quiet is dataThe broad edge state now says “water quiet” and gives no false signal outside the two emissions. Preset markers make the discoverable bands easy to revisit, while the display has a text fallback if its Canvas context cannot be created.
  • Arrival disciplineThe fixed field starts against the same abyss colour and fades in only after its first completed render. Font loading is optional rather than a late swap, so no unstyled layer or abrupt type reflow interrupts the descent.