What it is
A launch page for HVYLIA S-7, a fictional unmanned surface vessel from a fictional Ukrainian maker. Its proportions follow the real class of low-profile Ukrainian sea drones: about 5.6 m long, 1.5 m wide and half a metre above the water. One sticky canvas carries eleven chapters: the hero on a moonlit sea, a side profile with dimension lines, an X-ray of the hull with labelled modules, a powertrain cutaway with water flowing through the jet, an exploded maintenance view, the bow sensor's view in optical, low-light and thermal, a night search with a mast searchlight finding a life raft, a comms chapter with animated satellite, mesh and ship links, a top-down mission run with a route chart and live readouts, a launch and recovery from a support ship's stern A-frame, and a fleet that switches between four formations. A spec sheet and a demo contact form follow in plain HTML. Every figure on the page is illustrative.
Engineering decisions
- Everything is generated in code: the hull is lofted from cross-sections (deck, sides and bottom are separate strips, so the chines stay sharp while the surfaces stay smooth), with spray rails, rub strakes and a deck-edge rail lofted the same way. Fittings (cowling with vents and grab rails, hatches, sensor mast with radar dome, cameras, antennas and searchlight, sensor ball on a yoke, satcom panels, waterjet with nozzle, steering bucket, trim tabs and intake grille) are primitives. A small
Kit helper bakes every part's transform and merges them per material, so a vessel is about a dozen draw calls however many parts it has; the cowling and hatch lids are separate kits so they can lift in the exploded view. The fishing boat, life raft, 32 m support ship (with a tilting A-frame, bridge, mast, railings and winch) and the satellite are built the same way. No model file and no texture download. - Spray is a CPU ring buffer of points (bow quarters and a rooster tail from the jet, rate by speed); the jet flow in the cutaway and the comms pulses are animated entirely in vertex shaders. The searchlight is a real spot light plus an additive cone, and the sea shader adds the light pool on the water. The sky has cloud bands, a coastline with harbour lights and a night dimmer shared through one uniform.
- Each chapter has its own camera move (orbit, dolly, rise) across its scroll span, and chapter-to-chapter moves swoop up on long travels. Scrubbed sequences (exploded view, launch from the A-frame, searchlight lock-on) read one
localAt value that rests at the finished state on the chapter centre, which is also the reduced-motion pose. - The sea is one displaced plane with four Gerstner waves (three on the light tier) in the vertex shader. It follows the camera and snaps to its own grid, so it never ends and its vertices never swim. The same wave function runs on the CPU to give each vessel its heave, pitch and roll, so the hulls ride the waves the shader draws. The fragment shader adds moon glint, sky reflection by Fresnel, crest foam and wakes: a narrow trail and two Kelvin-style arms behind each of up to five vessels, computed from their positions in a uniform array.
- Sensor looks without post-processing: every material, including the sea and sky shaders, shares one uniform block. A small GLSL chunk injected through
onBeforeCompile applies fog, then mixes in the low-light look (green, grain, vignette) or the thermal look (an ironbow palette driven by a per-material heat value, so the exhaust stack glows while the sea stays cold). No extra render target and no full-screen pass. The X-ray uses the same hook: the hull turns into a Fresnel glass shell and the interior modules light up one after another. - Fog is part of the story: at night the fishing boat 160 m off is lost in the haze in optical mode. Low-light and thermal thin the fog, so the contact appears.
- Camera poses, speeds and fog density are data in
data.ts, one per chapter, blended by scroll. GSAP scrubs one plain state object; the render loop reads it. Scrolling never re-renders React. On portrait screens the camera pulls back and the picture shifts up above the text. - HTML overlays (dimension lines, module labels, the HUD tracking box, the mission ring) are positioned from screen projections the scene writes every frame. They repeat what the chapter text says, so they stay out of the accessibility tree with the canvas.
- Mission readouts (distance, time at cruise, fuel left) and formation slots are pure functions in
mission.ts with unit tests. Followers join from astern when the fleet appears and glide between slots with exponential easing. - Frame loop:
frameloop="demand", plus a small requestAnimationFrame pump that asks for frames only while the stage is on screen, the tab is visible and motion is allowed. - Quality tiers:
low (phones, 4 cores or fewer, or ?quality=low) uses lower DPR, no antialiasing, three waves, a coarser sea, fewer hull sections and two followers instead of four, fewer spray points and railing posts. ?debug shows the frame rate. - The canvas is
aria-hidden; each chapter is a real section with a heading. Sensor mode and formation are radio groups with a roving tabindex; Rotate buttons turn the vessel without dragging. If the graphics context is lost the poster is shown.
Performance notes
Three.js and React Three Fiber load only on this page. No drei, no post-processing, no model or texture files. Geometries and materials are disposed on unmount.
Reduced motion
The sea is frozen and the vessels hold still. No intro glide and no camera travel: each chapter cuts to its finished pose behind a 0.15 s fade (modules exploded, the S-7 launched, the searchlight on the raft, all links drawn). No spray, no card slide-ins, no rail transitions. Sensor looks and formations switch at once.
Support ship and fleet presence
Outside reduced motion, the support ship and the fleet boats ease in and out (about 1 s), rising from and sinking below the waterline in place, so they never switch on inside the frame and never move through the S-7. Under reduced motion they switch at once, as above.
Sensor mode follows the scroll through the Sensors chapter: optical, then low-light, then thermal. The picker still works, and a click holds until the next step. Under reduced motion the mode is manual only.