Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ Logo variant C (the prism mark with a terminal inside) at 60u plus the PrismTTY
Three facts on the hero's bottom edge, each a 44u stroke icon (2px, currentColor, custom SVG) beside a title and body, divided by vertical hairlines; stacked with horizontal hairlines on phones.

### The Seam (signature)
Raw output left, the prism in the middle, the same output through PrismTTY right, captioned "Real PrismTTY output, cisco profile." The still prism plate is the default. With motion allowed, the approved Higgsfield image-to-video take (take 1 of the approved prism) plays once, eases per frame to a stop on the wide spectrum, and the light layer fades in over 1.6s (cubic-bezier(0.16, 1, 0.3, 1)): a steady white beam flowing left to right (1.5s linear loop, screen-blended) and canvas dust that only shows inside the beam and the spectrum fan. On load each raw line flashes as it passes into the prism and its highlighted twin wipes in 0.32s later, 0.18s apart. Reduced motion, data saver, a video error or a stall keep the still plate. The light layer is hidden below 1280 px. The hero light plate and crystals drift slowly (26s and 34s, alternate) when motion is allowed.
Raw output left, the prism in the middle, the same output through PrismTTY right, captioned "Real PrismTTY output, cisco profile." The still prism plate is the default. With motion allowed, the approved Higgsfield image-to-video take (take 1 of the approved prism) plays once, eases per frame to a stop on the wide spectrum, and the light layer fades in over 1.6s (cubic-bezier(0.16, 1, 0.3, 1)): a steady white beam flowing left to right (1.5s linear loop, screen-blended) in a 4u band tilted 1.29° onto the beam line, and canvas dust drawn as whole-device-pixel points that only shows inside the beam and the spectrum fan. On load each raw line flashes as it passes into the prism and its highlighted twin wipes in 0.32s later, 0.18s apart. Reduced motion, data saver, a video error, a stall or footage that never produces a frame keep the still plate: where the browser has requestVideoFrameCallback, the plate steps aside only once it reports a frame and the footage is unhidden (without that API, on playing). The light layer is hidden below 1280 px. The hero light plate and crystals drift slowly (26s and 34s, alternate) when motion is allowed.

## Do's and Don'ts

Expand Down
14 changes: 9 additions & 5 deletions docs/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -458,24 +458,28 @@ a {
opacity: 1;
}

/* The flow band hugs the beam line of the plate and the footage: a 2u core
with 1u edges, tilted 1.29deg to follow the beam's fall. */
.beam {
position: absolute;
left: calc(500 * var(--u));
top: calc(504 * var(--u));
top: calc(506 * var(--u));
width: calc(246 * var(--u));
height: calc(12 * var(--u));
height: calc(4 * var(--u));
background: repeating-linear-gradient(
90deg,
transparent 0,
transparent calc(34 * var(--u)),
rgb(255 255 255 / 0.5) calc(62 * var(--u)),
rgb(255 255 255 / 0.75) calc(62 * var(--u)),
transparent calc(90 * var(--u))
);
background-size: calc(90 * var(--u)) 100%;
-webkit-mask-image: linear-gradient(180deg, transparent, #000 42%, #000 58%, transparent),
transform-origin: 0 50%;
transform: rotate(1.29deg);
-webkit-mask-image: linear-gradient(180deg, transparent, #000 25%, #000 75%, transparent),
linear-gradient(90deg, transparent, #000 22%, #000 88%, transparent);
-webkit-mask-composite: source-in;
mask-image: linear-gradient(180deg, transparent, #000 42%, #000 58%, transparent),
mask-image: linear-gradient(180deg, transparent, #000 25%, #000 75%, transparent),
linear-gradient(90deg, transparent, #000 22%, #000 88%, transparent);
mask-composite: intersect;
mix-blend-mode: screen;
Expand Down
34 changes: 27 additions & 7 deletions docs/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,31 @@
let settled = Boolean(navigator.connection?.saveData); // nothing left to play
let easing = 0; // animation frame of the ease
let stall = 0; // timer that gives up on footage that stopped arriving
// Whether the footage has produced a frame. A browser can run the clock
// and drop every frame (Safari 27 did, for any clip), so the plate stays
// until requestVideoFrameCallback reports one. Without that API, 'playing'
// is taken at its word.
let shown = !(video && 'requestVideoFrameCallback' in video);

// The intro is over: hold the frame it stopped on and bring in the light.
// Footage that never produced a frame gives way to the still plate.
function settle() {
settled = true;
clearTimeout(stall);
cancelAnimationFrame(easing);
easing = 0;
video.pause();
root.classList.add('prism-live');
if (!shown) {
video.hidden = true;
root.classList.remove('prism-playing');
}
}

// The footage broke: drop it and fall back to the still plate.
function giveUp() {
shown = false;
settle();
video.hidden = true;
root.classList.remove('prism-playing');
}

// One step per frame near the end. playbackRate = sqrt(left / EASE) is a
Expand Down Expand Up @@ -145,8 +154,19 @@
}
clearTimeout(stall);
video.hidden = false;
root.classList.add('prism-playing');
if (shown) root.classList.add('prism-playing');
});
// The footage sits above the plate and paints nothing until it has a
// frame. The plate steps aside once a frame is reported and the footage
// is unhidden, whichever comes last: a frame can be reported for the
// hidden element (Firefox on load, any engine while paused off screen).
if (!shown) {
video.requestVideoFrameCallback(() => {
if (settled) return;
shown = true;
if (!video.hidden) root.classList.add('prism-playing');
});
}
// timeupdate (about 4 Hz) only opens the window; the frames do the ease.
video.addEventListener('timeupdate', () => {
const left = video.duration - video.currentTime;
Expand Down Expand Up @@ -179,7 +199,7 @@
motes = Array.from({ length: 34 }, (_, i) => ({
x: ((i * 0.6180339887) % 1) * canvas.width,
y: (0.08 + 0.84 * ((i * 0.3819660113) % 1)) * canvas.height,
r: (0.5 + ((i * 7) % 5) * 0.22) * scale,
s: Math.max(1, Math.round((0.5 + ((i * 7) % 5) * 0.22) * scale)), // side, whole device px
v: (0.1 + ((i * 3) % 7) * 0.025) * scale,
}));
}
Expand Down Expand Up @@ -207,9 +227,9 @@
const fade = Math.min(1, Math.min(m.x, w - m.x) / (w * 0.08));
const lit = 0.06 + 0.8 * light(m.x / w, y / h);
ctx.globalAlpha = lit * fade;
ctx.beginPath();
ctx.arc(m.x, y, m.r, 0, Math.PI * 2);
ctx.fill();
// Snapped to whole device pixels, each mote is a crisp point rather
// than a sub-pixel smear.
ctx.fillRect(Math.round(m.x - m.s / 2), Math.round(y - m.s / 2), m.s, m.s);
}
frame = visible && drawable ? requestAnimationFrame(draw) : 0;
}
Expand Down
Loading