What to Watch turns a movie catalog into a space you drift through. Tiles sit on a curved field and the one nearest your focus comes forward.
Tiles live in a flat grid of (u, v) coordinates. Each frame the pan offset is added and wrapped with a modulo, so the grid repeats forever. Each u is then bent onto a cylinder: it becomes an angle, the tile is placed on the circle and rotated to face the axis. The tile nearest the centre eases toward full focus, which scales it up, pulls it toward the camera and lifts its brightness. All of this runs on the CPU for about forty meshes.
const k = 0.04 + p.curvature * 0.1; // radians per unit of arc
const radius = 1 / k;
for (const tile of tiles) {
// infinite grid: wrap the panned coordinate into one period
tile.u = wrap(tile.baseU + pan.x, spanX);
tile.v = wrap(tile.baseV + pan.y, spanY);
}
tiles.forEach((tile, i) => {
tile.focus += ((i === nearest ? 1 : 0) - tile.focus) * ease;
const angle = tile.u * k;
const pull = 0.6 * p.focus * tile.focus;
const r = radius - pull; // toward the camera
tile.mesh.position.set(
Math.sin(angle) * r,
tile.v,
Math.cos(angle) * r - radius
);
tile.mesh.rotation.y = -angle;
tile.mesh.scale.setScalar(1 + 0.25 * p.focus * tile.focus);
tile.material.color.setScalar(0.55 + 0.45 * tile.focus);
});