No.167 · Backgrounds· New
Pin Field
A pin-art board in motion: diagonal swells push rows of pins up out of a dark board, and each raised pin catches light from the upper left and throws a short shadow, so the waves read as relief. The pointer presses a hollow into the pins.
The prompt
Written for the variant you picked above: Graphite
Write a WebGL1 full-screen fragment shader background (no libraries) that looks like a pin-art board. Divide the canvas into square cells, 76 across the longer side (about 60% as many under 640px). For each cell compute a lift from 0 to 1 at its centre: two sharpened diagonal sine ridges (pow of a 0–1 sine, powers 6 and 9, at slightly different angles, spacings and slow speeds) combined with max, then modulated by slow value noise so the swells come and go. Draw each pin as a disc whose radius grows from 11% to 38% of the cell with its lift. Light comes from the upper left: raised pins get a soft highlight towards that corner, a darker rim on the far side, and a shadow disc offset down-right by up to 30% of a cell, also scaled by the lift, darkening the board under it. Pins at rest are small, dim dots, so the whole board is visible as texture. Colour the pin from the low colour at rest to the high colour fully raised, and vignette the corners. The pointer presses into the board: lift drops to nearly nothing in a soft circle under it, with a raised ring just outside, easing in as it enters and out as it leaves. Render at full device pixels up to 2× (70% and about 30fps on touch), pause offscreen and in hidden tabs, draw one still frame under reduced motion, and fall back to a CSS dot pattern without WebGL. Children sit above the canvas. Selected variant — Graphite (graphite): Palette { ground #0b0b0d, low #2b2b30, high #f4f4f2 }: white pins on a charcoal board. This selection takes precedence over references to other themes or options in the general brief. Implement this selected appearance; do not add a variant picker or alternative appearances unless requested. Implementation requirements: Use React and TypeScript. Keep the component self-contained, with uniquely scoped CSS and no animation libraries. Include required imports, styles and font setup in the result. Interaction: Move the pointer to press a hollow into the board; the pins around it rise into a rim. Motion: Two diagonal swells drift across the board continuously; the pointer's hollow eases in and out (0.06 per frame) and follows with a 0.14 lerp. Reduced motion draws one still frame. Accessibility: Decorative and hidden from assistive tech. Raised pins can be bright, so put a scrim behind text. Responsive behavior: Fills its container; pins get bigger (fewer across) on narrow screens; device pixel ratio capped at 2. Touch: No pointer effect on touch; the swells keep moving on their own. Preserve keyboard access, visible focus and reduced-motion behavior. Keep fictional demo data and simulated service calls in a separate usage example; expose callbacks for real actions and recover from rejected requests. Keep usage-example links inside the demo: use local buttons or prevent navigation. Demonstration links must not navigate to Vitrine routes or open other pages; preserve real URL support in the reusable component itself. Verify the result at narrow and wide viewport sizes, with keyboard input and reduced motion enabled.
Notes
- Interaction
- Move the pointer to press a hollow into the board; the pins around it rise into a rim.
- Motion
- Two diagonal swells drift across the board continuously; the pointer's hollow eases in and out (0.06 per frame) and follows with a 0.14 lerp. Reduced motion draws one still frame.
- Accessibility
- Decorative and hidden from assistive tech. Raised pins can be bright, so put a scrim behind text.
- Responsive
- Fills its container; pins get bigger (fewer across) on narrow screens; device pixel ratio capped at 2.
- On touch
- No pointer effect on touch; the swells keep moving on their own.