Hollis & Vane ← Return to the orrery

Field notes / 01

Built around
an orbit.

A making-of page for Hollis & Vane: a fictional two-person workshop whose mechanical objects use patience and precise ratio as their luxury. The website gives that same premise a visible mechanism.

01 — The concept

A website that behaves like a quiet instrument.

Hollis & Vane is deliberately not framed as a jewellery brand with planetary decoration. It is a workshop built around a physical proposition: astronomical time made legible on a desk. The layout uses the spatial logic of an orrery—wide voids, measured rules, large circular forms, and information arranged like a ledger.

The copy treats a commission as a relationship with a room. That made the important details concrete: who works at the bench, which stones are used, how long a movement is regulated, and why only six objects leave the workshop each year.

02 — Signature technique

A real, draggable orrery.

The hero is a live Three.js scene, not a video or static render. Each world runs on an elliptical TubeGeometry orbit that is physically inclined in its own group. Its position is recalculated from a shared Earth reference and its actual orbital period in days. The clock runs dramatically faster than nature, but Mercury still overtakes Earth at the correct ratio while Jupiter barely shifts.

Pointer dragging changes a camera azimuth target; two visible buttons provide the same control for keyboard and touch users. The renderer caps pixel density at two, stops rendering when the tab is hidden or the hero leaves view, and renders a still first frame for reduced-motion visitors.

Excerpt from index.html — guarded boot + literal period relationship

async function bootOrrery() {
  try {
    const THREE = await import('three');
    createOrrery(THREE);
  } catch {
    showOrreryFallback();
  }
}

renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));

function updatePlanet({ data, planet, arm, armVector }, elapsed) {
  const theta = data.phase + elapsed * .18 * (365.256 / data.days);
  const x = Math.cos(theta) * data.a;
  const z = Math.sin(theta) * data.b;
  planet.position.set(x, 0, z);
  armVector.set(x, 0, z);
  arm.position.copy(armVector).multiplyScalar(.5);
  arm.quaternion.setFromUnitVectors(armAxis, armVector.normalize());
  arm.scale.y = arm.position.length() / data.a;
}

03 — Palette & type

Obsidian to bone, with brass doing the mechanical work.

The dark palette leaves room for the light from the brass rings and keeps the WebGL scene from feeling like product visualisation. Oxide provides the one restrained flare: it is the colour of printed astronomical markings, not an accent pasted on top.

Obsidian#0B0C10
Brass#B08D4F
Bone#E8E2D4
Oxide#7A3B2E

Cormorant Garamond / display

The sky, held in hand.

Inter / body + labels

Wide-letterspaced small caps give the supporting details the character of bench annotations, while the serif stays unhurried and tactile.

04 — Iteration record

What broke, and what changed.

Pass 1 — correctness

  • Reworked the WebGL boot path so a failed Three.js import, renderer construction, or lost WebGL context reveals an immediate CSS still view and a clear status instead of leaving a blank hero.
  • Corrected each radial arm’s scale: it now ends at its planet rather than extending to twice the orbital radius. The renderer’s device-pixel-ratio cap is also reapplied on resize.
  • Added explicit lifecycle guards for reduced-motion changes, document visibility, canvas visibility, ResizeObserver fallback, and IntersectionObserver fallback so the animation loop is not left running off-screen or in a hidden tab.
  • Added 44 px or larger navigation, footer, and orbit-control targets; keyboard/touch orbit buttons; a skip link; a content-info footer landmark; and a brighter oxide treatment where the original oxide failed contrast on obsidian.
  • Added a 900 px stacking breakpoint, grid shrink guards, and mobile code-block containment. Source-level checks passed, leaving live viewport capture for the final finishing pass.

Pass 2 — design depth

  • Weak moment: the three material studies read as identical cards. Recast them as an asymmetrical, two-storey composition: the brass study now holds a tall left-hand field, while stone and glass form unequal horizontal plates. Their drawn bodies deliberately press against their frames, and a restrained oxide orbital trace now reaches into the section background.
  • Weak moment: the live orrery lacked a visible instrument readout. Added a live azimuth plate tied directly to the draggable camera position, with a brass-and-oxide dial and an Earth-period reference. Removed the ornamental ring wobble so the scene’s motion is only measured rotation and user-led camera movement.
  • Weak moment: the vertical rhythm and display treatment were too locally decided. Introduced a named spacing scale, applied it to the major section intervals, tightened and enlarged the hero display, balanced headings, and replaced every remaining default ease transition with deliberate mechanical settling curves.

Pass 3 — complexify

  • New moment: the workshop needed something to discover after the hero. Added the Regulation Record: a draggable, keyboard-operable 1,000-day bench test. It redraws the five bodies as an inclined brass chart from their literal day counts, so moving from the datum to the end makes Mercury’s repetition and Jupiter’s hesitation visible rather than merely asserted in copy.
  • Edge state: a time control should say what its ends mean. The record names both its zero-day datum and its 1,000-day finish, then rewinds deliberately if played again. Reduced-motion visitors can still inspect every position with the range input, while its automatic run stays disabled.
  • Finish: the first visual frame needed to feel intentional. Kept the drawn armillary visible while WebGL prepares, then crossfaded to the rendered scene only after its first frame exists. Static validation covered the hero, chart, focus treatment, and responsive guards; the same 390 / 768 / 1440 px browser sweep is the final deployment check.