枠の中でスクロール・クリックして動きを試せます。端末指定は実寸表示です。幅が足りない場合は横にスクロールできます。
再現プロンプト
架空のボクシング興行EMBER ARENAの1ページ。見出し・アイコン・選手ポートレート(モノクロ画像を輝度サンプリングでLED化)のすべてを円形LEDドット格子でレンダリングし、波紋点灯・掃引切替・残像発光の3シグネチャーで貫く電光掲示板デモ。 配色・構成・動きの数値をまとめた指示書です。 お使いのAIに貼り、素材やお店の情報を伝えて実装してください。仕上がりは利用するAIと素材によって変わります。
Build a single self-contained HTML file (inline CSS/JS) for "EMBER ARENA", a fictional boxing venue promoting its flagship card "FIGHT NIGHT VIII". The page's entire visual system IS its own LED scoreboard: a low-resolution grid of circular amber dots is the only way any heading or pictogram is ever drawn — no vector text stands in for the display. Every wordmark, bout icon, fighter portrait, ticket glyph and the footer's closing line is rasterised (bold text, a hand-drawn vector shape, or a high-contrast monochrome fighter portrait) onto an offscreen canvas, sampled down to a coarse brightness grid, and lit dot-by-dot on a visible <canvas> — a photograph is never shown directly, only replayed as LEDs. May use a CDN script for gsap and its ScrollTrigger plugin. The page scrolls with the normal window (no inner scroll container needed for the standalone output).
## Art direction
Palette: page background #0A0907, panel background #141109, hairline borders rgba(255,176,0,0.20), primary text #F5EEDD, muted text #9C9284, single accent amber #FFB000 for every dot, glow, border and CTA — no other hue on the page. Monospace (ui-monospace / "SF Mono" / Menlo) for eyebrows, meta rows and prices; a bold system sans (font-weight 800) for real DOM headings. A fixed 60px top nav (translucent panel, blurred, 1px bottom hairline) shows "EMBER ARENA" with a "FIGHT NIGHT VIII" sub-label, links to Fight Card / Roster / Tickets, and a "Get Tickets" pill. A 2px top progress bar fills amber as the page scrolls (ScrollTrigger scrub 0.3, trigger = full document height).
## The dot-matrix engine (must match exactly — reused by every panel)
Each LED panel is one instance of the same renderer: a canvas sized to its host element, devicePixelRatio clamped to 2, with a grid of cols x rows dots whose spacing (pitch) is fixed in CSS px per panel type: hero wordmark 10px (DOT_PITCH_HERO), footer ignition line 8px (DOT_PITCH_HEADING), bout-type icons 4.5px (DOT_PITCH_ICON), fighter portraits 5px (DOT_PITCH_PORTRAIT), ticket icons 4.5px (DOT_PITCH_TICKET), the ARENA WIRE ticker 8px (DOT_PITCH_TICKER). Columns/rows are re-derived on every resize as round(width/pitch) and round(height/pitch), clamped per panel (e.g. the hero: 20-140 columns, 6-28 rows). Dot draw radius = pitch * 0.34 (DOT_RADIUS_RATIO). An "off" baseboard of dim amber dots is drawn every frame at 7% opacity (DOT_OFF_ALPHA) as a single batched path + one fill() call regardless of grid size — the always-visible unlit PCB texture.
Content-to-grid sampling: to display text, render it to an offscreen canvas at 4x supersample resolution (cols*4 by rows*4 px) using a 900-weight condensed sans ("Arial Black"/Impact), shrink-to-fit at 94% of the offscreen width, then for every grid cell average the alpha channel of its 4x4 pixel block into a 0..1 brightness value. Icons use the identical sampling pipeline against hand-drawn vector shapes (a boxing glove, a ring bell, a five-point star, a corner-post-with-ropes, a ticket stub with a punched hole via destination-out compositing) instead of text. The three roster portraits add a second sampling mode to the same pipeline: a dramatic, poster-like monochrome fighter portrait (2-3 big light/shadow masses on a pure-black background — paint each one in grayscale on the offscreen canvas; do not embed or display any actual photograph) is cover-fitted onto the supersampled canvas, and each grid cell averages alpha-weighted Rec.709 LUMINANCE instead of alpha coverage; the result is then run through a levels curve (floor 0.09, ceiling 0.8) and posterised to 4 lit brightness steps, so at roughly 30x30 dots the face reads as banks of amber LEDs, never as a pasted image.
Per-frame brightness: each cell holds a "current" value that eases toward a "target" value using an attack rate of 11/s when rising and a decay rate of 2.4/s when falling (current += (target-current) * min(1, rate*dt)) — this asymmetric ease is the afterglow: ignitions snap on fast, then bleed off slowly. Rendering batches lit cells into 12 brightness buckets (BRIGHTNESS_BUCKETS) per frame; for each non-empty bucket, fillStyle and shadowBlur are set once (alpha = 0.3 + 0.7*bucketT, shadowBlur = panel-glow * (0.3 + 0.7*bucketT), shadow color rgba(255,176,0, 0.55*bucketT)), and every dot in that bucket is added to one path and filled together — canvas state changes stay at 12 per frame regardless of dot count. Dots above ~72% brightness mix toward white (hot overdriven core) instead of pure amber. Hero and footer panels use a glow ceiling of 9px (GLOW_BLUR_HERO); every smaller panel uses 6px (GLOW_BLUR_PANEL).
Ripple ignition: a point-light event stores an origin (x,y in host-local px) and an age t. Each frame its wavefront radius = t * 340px/s (RIPPLE_SPEED); any cell within 46px (RIPPLE_WIDTH) of that radius gets an additive brightness boost of up to 0.85 (RIPPLE_MAX_BOOST), falling off linearly across the band width and fading out linearly as t approaches a 0.9s lifetime (RIPPLE_LIFE), after which the ripple is discarded. Up to 8 concurrent ripples per panel (RIPPLE_MAX_ACTIVE); overlapping boosts take the maximum, not the sum.
Sweep switching: pattern changes are never a crossfade. A "from" and "to" brightness field are stored with a duration (0.85s on the hero, SWEEP_DURATION_HERO; 0.7s on every section panel, SWEEP_DURATION_PANEL) and an axis: "x" (linear, left-to-right — used on the hero and every bout/ticket row) or "radial" (outward from the grid's own center — used on fighter portraits and the footer ignition). Every cell's coordinate along that axis (0..1) is compared against the sweep's elapsed-fraction: cells behind the wipe show the "to" pattern, cells ahead still show "from". Cells within 8% of the axis (SWEEP_HEAD_WIDTH) behind the wipe's leading edge get an extra +0.4 brightness boost (SWEEP_HEAD_BOOST) that fades to zero at that edge — a bright comet head leading every switch.
## Section 1: Hero
A metal bezel panel (max-width 920px, 5:2 aspect ratio, rounded corners, four small pulsing corner bolts) houses the full-bleed LED canvas. It idle-cycles automatically through three frames — "EMBER ARENA", "FIGHT NIGHT VIII", "GATES OPEN 18:00" — every 3.6s (HERO_CYCLE_S), each switch using the linear sweep described above. Below the bezel: a small monospace plate reading "EMBER ARENA — MODEL VII DISPLAY SYSTEM". Moving the pointer over the panel continuously fires ripples at the cursor; when the pointer is idle the panel still pings itself with an automatic ripple from its own center every 4.4s (HERO_IDLE_RIPPLE_INTERVAL_S), so the display is never static. Below the panel: an eyebrow "FIGHT NIGHT VIII // EMBER ARENA", one line of copy about the arena's own amber dot-matrix calling the card, a "GET RINGSIDE TICKETS" CTA that jumps to the tickets section, and a pulsing "( SCROLL FOR THE CARD )" cue.
## Section 2: Fight Card
Heading "Every bout, called live on the board." above a bordered list of four bout rows: MAIN EVENT — Marcus "The Furnace" Reyes vs Dmitri Volkov, Light Heavyweight, 12 rounds, bell icon; CO-MAIN EVENT — Aleksandra "Iron Willow" Novak vs Priscilla Okafor, Women's Welterweight, 10 rounds, glove icon; UNDERCARD — Jamal Briscoe vs Teo Santoro, Cruiserweight, 8 rounds, star icon; OPENING BOUT — Ryuji Kano vs Caleb Whitfield, Middleweight, 6 rounds, ring-post icon. Each row has a small square LED icon panel; as the row scrolls into view (ScrollTrigger start "top 85%") its icon sweeps on (linear axis, 0.7s) while its text ignites; scrolling back up reverses both. Hovering a row fires a ripple centered on its icon panel.
## Section 3: Roster
Heading "Three fighters carrying the marquee." above three cards (Marcus Reyes "The Furnace", 27-2-0, orthodox; Aleksandra Novak "Iron Willow", 15-0-1, southpaw, headband; Dmitri Volkov "The Siberian Lock", 24-3-0, orthodox, headband), each with a square LED portrait panel lit from that fighter's own luminance-sampled monochrome portrait — three visibly different people even at ~30x30 dots: Reyes faces left in a tight high guard, short crop, no headband; Novak faces right (southpaw, mirrored against the other two) with a bright headband band, tied-back hair and an extended lead arm; Volkov is frontal and massive, shaved head under a headband, arms crossed at the chest, eye sockets in deep shadow. Cards sweep their portrait on with the radial axis on scroll entry. Moving the pointer across a card both fires a ripple at the cursor and tilts the card up to 10 degrees (ROSTER_TILT_MAX) toward the pointer (rotateX/rotateY, 500ms power3.out, reset over 600ms on leave) — perspective 700px.
## Section 4: Seating & Tickets
Heading "Pick your distance from the ring." above three tier cards — RINGSIDE $420 (rows 1-4, fighter walk-out lane view, printed program + credential, padded seating), LOWER BOWL $180 (rows 5-20, full ring sightline, in-arena bar access, early gate entry), UPPER DECK $65 (general admission, best full-arena view, standard entry, all bouts on the big board) — each with a small LED ticket-stub icon (rounded rect with a punched hole) that sweeps on (linear axis) on scroll entry. Cards lift 4px and glow on hover; hovering also fires a ripple on the icon.
## Section 5: Arena Wire (the one permitted marquee)
A single 64px-tall ribbon panel with a small static "ARENA WIRE" tag pinned top-left and a continuously scrolling LED ticker reading (looped): "DOORS OPEN 18:00 // OFFICIAL WEIGH-IN — FRIDAY 15:00, ARENA PLAZA // UNDERCARD ADDED — BRISCOE VS SANTORO // PARKING — ENTER VIA GATE C // BROADCAST — CIRCUIT+ PPV 20:00 //". This runs through the identical dot-matrix engine in a special "ticker" mode: the full looped string is rasterised once into a wide offscreen buffer, and every frame the visible window samples that buffer at a horizontally increasing offset of 46px/s (TICKER_SPEED), wrapping with modulo so the loop is seamless; the same attack/decay persistence used everywhere else gives the moving text a soft trailing blur. This is the only horizontally-flowing marquee element anywhere on the page.
## Footer
A wide bezel panel (760px max, 5:1.5 aspect) that stays blank until scrolled into view (start "top 78%"), then radially sweeps in the line "SEE YOU AT THE BELL." over 0.85s while simultaneously firing a ripple from its own center — an ignition, not a fade. Below it: a "RESERVE YOUR SEAT" mailto CTA and a footer row with "© 2026 EMBER ARENA" and a contact email.
## Scroll animations
Every non-canvas text element (eyebrows, paragraphs, CTAs, bout/roster/ticket meta, footer row) shares one "ignite" reveal instead of a translateY fade: opacity 0 -> 1 plus an amber text-shadow glow (0 0 10px rgba(255,176,0,0.85), 0 0 26px rgba(255,176,0,0.45)) over 0.55s (IGNITE_DURATION) with ease power2.out, on ScrollTrigger onEnter (trigger = the element itself, start "top 88%", toggleActions "play none none reverse"); elements are grouped (hero, each section head, each bout row, each roster/ticket card, the footer) and staggered 0.09s (IGNITE_STAGGER) per line within their group via a per-element delay. onLeaveBack reverses over 0.3s. No element anywhere uses a plain y+opacity fade, and there is no numbered "01/02/03" list styling and no count-up stat numbers — records and prices are static text.
## Performance & accessibility
Each LED panel owns its own ResizeObserver (recomputing cols/rows and resampling its current pattern) and its own IntersectionObserver that pauses its requestAnimationFrame loop entirely while off-screen. devicePixelRatio is clamped to 2 and the canvas context transform is reset (never compounded) on every resize. Per-frame cost is dominated by grid size and ripple count, both bounded (max 140x28 on the hero, 8 ripples/panel). Under prefers-reduced-motion: the hero's automatic frame-cycling and idle heartbeat ripples are disabled (it renders one static frame), all sweep transitions and ripples on every panel snap instantly instead of animating, the ticker's scroll speed drops to 20% of normal, and every text ignite reveal is set to its resting state immediately instead of scrubbing on scroll. The hero wordmark and the footer's closing line are canvas-only, so a visually-hidden heading/paragraph duplicates their text, as does a hidden list of the ticker announcements. All nav links, ticket CTAs and the footer CTA are real buttons/links with visible hover and focus-visible states against the #0A0907 background.
## Action flow and content
Implement the following local planning experience for the primary CTA, in the visual language of the page. Use a real button opening a labelled modal dialog. Choosing a pricing card must preselect that exact plan. A navigation link may move to another section; a planning CTA must lead to its review flow.
Use this content for the dialog, including the option details and notes:
{
"slug": "dot-matrix",
"brand": "EMBER ARENA",
"accent": "#905326",
"title": "Plan your place at fight night",
"intro": "Compare the view and keep a seat preference for the evening.",
"facts": [
[
"Programme",
"FIGHT NIGHT VIII"
],
[
"Gates",
"18:00 in the sample programme"
],
[
"Before booking",
"Confirm the actual event date, seating view and access arrangements."
]
],
"groups": [
{
"label": "Seating",
"options": [
{
"title": "RINGSIDE",
"detail": "Closest to the ring · sample price $420."
},
{
"title": "LOWER BOWL",
"detail": "An elevated view of the ring · sample price $180."
},
{
"title": "UPPER DECK",
"detail": "A wider view across the arena · sample price $65."
}
]
}
],
"notes": [
"The programme, fighters and prices are fictional. No ticket is held or purchased."
]
}
- Explain before input that this is a fictional brand sample: choices can be reviewed and saved, with no external submission, booking, payment or registration. Use each brand's language. Do not launch a mail client or present an installer for a fictional product.
- Give every option group a legend, inputs explicit labels and errors linked descriptions. Validate options against the allowed values, requested dates as actual dates from today onward, and free text as 10–2,000 trimmed characters. Blank spaces alone must fail. Tracking accepts the sample reference TG-2048 only.
- Focus the first invalid field and preserve every entered value. On success, show the selected option WITH its full detail, requested date or brief, relevant facts and notes. An edit action returns to the preserved form. Do not claim a real reservation, purchase or account has succeeded.
- Save the complete review as UTF-8 text. A browser download request is not proof of a file being written. On download failure, retain the full text in a labelled, selectable textarea and offer retry. Do not show a false success state.
- Constrain modal content to the viewport with internal scrolling. Support keyboard and touch, trap focus while open, close on Escape and restore focus to the exact initiating control. Keep these operations available with motion disabled.
- If a separate project-contact CTA is present, let it collect a project brief and review it locally; a product selection CTA must retain the product choice. Verify both flows independently.