MOTION PALETTE
← すべてのデモ

Train View

BackgroundAI Images / SVG / GSAP / ScrollTrigger

フルスクリーンで開く ↗

枠の中でスクロール・クリックして動きを試せます。端末指定は実寸表示です。幅が足りない場合は横にスクロールできます。

再現プロンプト

架空のローカル線トラベルキャンペーン「MERIDIAN LOCAL」の縦長1ページサイト。空・山・街・柵の4層視差風景が常時横に流れ続ける車窓をモチーフに、駅到着ではスクロールに応じたスロットルが減速→停止→再加速し、Harborlight区間では日→夜の色調がスクラブで変化する。 配色・構成・動きの数値をまとめた指示書です。 お使いのAIに貼り、素材やお店の情報を伝えて実装してください。仕上がりは利用するAIと素材によって変わります。

Build a single self-contained HTML file (inline CSS and inline JS in one `<script>` tag) for a complete, long-form one-page site for a fictional scenic branch-line travel campaign called **MERIDIAN LOCAL**. Load GSAP 3 and its ScrollTrigger plugin from CDN only (e.g. `https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js` and `.../ScrollTrigger.min.js`); no fetch calls. The scenery layers and the onboard blur are locally AI-generated WebP plates (see Imagery below); every structural visual — window frame, straps, displays, platform plaque — stays inline SVG or CSS. In this standalone build the whole page scrolls with the normal window/document scroller. The motion language for the entire page is **"the window never stops"**: a multi-layer landscape (sky / mountains / town / fence) scrolls continuously past the window at a speed the page controls, not a scroll-position-bound parallax (never freeze the scenery the instant scrolling stops) and never a split-flap departure board — this is a continuously accumulating offset whose *rate* is modulated by scroll.

## Art direction

Palette: ink/background `#12181C`, foreground `#F2F5F6`, muted steel `#9FB0B8`, accent blue-grey `#90A4AE`. Day sky gradient `#BFE0EC → #F0EDE2`; night sky gradient `#0C1524 → #1E3048`; mountains `#7C93A3`; town `#54697A`; foreground fence `#1C2226`; lit windows `#FFD98A`; sun `#FFE7A8`; moon/stars `#E7ECF2`. Headings weight 800 at fluid `clamp()` sizes; eyebrows 0.72rem uppercase, 0.26em letter-spacing, accent color. Every "window" section shares one frame recipe: `border-radius:20px`, `box-shadow: inset 0 0 0 2px rgba(144,164,174,0.28), inset 0 40px 70px -30px rgba(0,0,0,0.55), inset 0 -40px 70px -30px rgba(0,0,0,0.55)`, plus a thin vertical mullion at 34% width and a horizontal mullion at 22% height (both `rgba(18,24,28,0.5)`), and a diagonal glass glint (a 30%-wide `linear-gradient(100deg,transparent,rgba(255,255,255,0.12),transparent)` bar rotated 8deg) that sweeps left-to-right over 9s linear infinite — idle, unrelated to scroll, so the glass never looks perfectly still.

## Imagery (AI-generated poster plates)

Generate six images with an image AI, all sharing one poster style block: "flat mid-century railway travel poster illustration, clean vector-like flat shapes, 2-4 muted tones per element, subtle screen-print grain, quiet coastal atmosphere, crisp edges without outlines, no text, no photorealism". Five are transparent landscape plates (generate on a flat chroma-key background, alpha-key locally, then crop 2:1 anchored to the bottom edge — clouds centered instead): (1) **mountains** — two or three overlapping ridge lines in muted blue-grey #7C93A3 with a paler haze ridge behind and soft mist bands at the foot, occupying only the lower half, touching the bottom and both side edges, ending low at both sides; (2) **town-day** — a continuous row of small gabled coastal houses with one slender lighthouse and one small harbor crane near the center, unlit windows, flat slate-blue tones around #54697A, low houses at both edges, touching bottom and both sides; (3) **town-night** — the EXACT same town silhouette regenerated at night (facades sunk to #2E4050, many small windows warmly lit #FFD98A, porch lamps) — generate it from the day plate as an edit so the outlines register; (4) **fence** — the nearest lineside strip: two horizontal fence rails on posts, trackside grass tufts, one telegraph pole, all near-black #1C2226 single-tone silhouette in the bottom quarter, touching bottom and both sides; (5) **sky-clouds** — four to six soft flat stylized clouds, cream #F4EFE2 with pale blue-grey undersides, floating in the middle band and touching NO image edge (they loop through transparency). The sixth is opaque: (6) **onboard-blur** — a photographic long-exposure view through a moving train window at dusk, horizontal motion-blur streaks in deep blue-grey slate (#2C3A44→#1B242B) with faint warm amber light streaks, no recognizable objects.

## The shared scenery system (signature ① — layer speeds constantized)

Every "window" (Hero, Fernhollow, Harborlight, Footer — 4 instances total) renders the same 4-layer scenery in one shared `viewBox="0 0 800 200"` reference frame, farthest to nearest: **sky** (the clouds plate), **mountains**, **town**, **fence** (the fastest, closest layer). Each layer is drawn once inside an SVG `<pattern patternUnits="userSpaceOnUse">` sized to a fixed tile width and tiled horizontally via a full-bleed `<rect fill="url(#pattern)">`. Each landscape plate is a 2:1 image placed at 400x200 viewBox units (`preserveAspectRatio="none"`); mountains, town and fence lay the plate down as a **mirrored pair** (`<image>` plus a `translate(800,0) scale(-1,1)` flipped copy) inside one 800-unit tile so the horizontal loop is seamless by construction — both plate edges meet their own reflection, no edge-matching needed. The clouds plate touches no image edge, so its tile is a single 400-unit piece that loops through transparency. Tile widths: sky 400, mountains 800, town 800, fence 800. The town tile stacks the night plate above the day plate inside the same pattern, wrapped in a `<g style="opacity: var(--night-t, 0)">` so signature ④ cross-fades day to night without touching the ticker. A single shared "throttle" value (0..1, one plain JS object, never re-created) is scrubbed by ScrollTrigger zones described below; one `gsap.ticker.add` callback runs for the page's lifetime and, every frame, for each layer computes `advance = 140 * ratio * throttle * dt` (`dt` = seconds since last tick) where `ratio` (per depth, sky→fence) = `[0.12, 0.3, 0.65, 1.6]` — nearer layers move up to 13x faster than the sky — accumulates `offset = (offset + advance) % tileWidth`, and writes `pattern.setAttribute("patternTransform", "translate(" + (-offset) + ",0)")` on every instance of that layer (16 pattern elements total across the 4 windows). The sky layer additionally always drifts `6px/s` regardless of throttle (wind, not the train), so clouds never fully stop even at a dead halt. This is one continuous ticker, not per-instance state — all 4 windows always show the identical scenery phase, reading as one uninterrupted ride.

## Signature ②: station stops — throttle eases to 0 and back

The throttle is a single deterministic piecewise function of scroll position — NOT a chain of scrub tweens fighting over one shared value (clamped scrub renders overwrite each other in creation order whenever the scroll position jumps via scrollbar drag or reload restore, freezing the platform signs). Build an ordered list of zones, each a plain `ScrollTrigger.create({trigger, start, end})` used only as a responsive px range plus `{from, to, ease}`: **Hero departs** — `0.45 → 1`, ease `power1.out`, `start:"top top", end:"bottom top"` (the campaign opens mid-departure, not from a dead stop). **Approaching each station** (Fernhollow, Harborlight) — `1 → 0`, ease `power2.out`, `start:"top 88%", end:"top 38%"`. **Departing each station** — `0 → 1`, ease `power2.in`, `start:"top 38%", end:"bottom 55%"`. **Footer arrival** (final stop, no further departure) — `1 → 0`, ease `power2.out`, `start:"top 85%", end:"center center"`. On every scroll event (and after ScrollTrigger refresh), resolve the scroll position against that list — inside a zone interpolate `from + (to-from) × ease(progress)`, past a zone hold its `to` value, before every zone hold the hero's `from` — and write the result to `speedState.target`; between zones this naturally holds the last zone's end value (e.g. cruising at 1 through the Route and Onboard sections). The ticker then eases the displayed value toward the target every frame (`value += (target - value) × min(1, dt/0.45)`) for the same inertia the old scrub gave. The SAME throttle value drives every window's platform sign: each frame, `visibility = max(0, 1 - throttle)`; the platform's `opacity` is set directly to `visibility` and it slides up `translateY((1 - visibility) * 10px)` — so the platform (a station name plaque over a dashed yellow safety-stripe) visibly resolves into view only as the train slows toward a stop, and fades back out as it pulls away. This ties signatures ① and ② into one shared driver instead of two separate systems.

## Signature ④: the Harborlight night stretch

Harborlight's window container carries its own `--night-t` CSS custom property, `gsap.set` to `0` on mount, then scrubbed to `1` via `gsap.to(el, {"--night-t":1, ease:"none", scrollTrigger:{trigger:harborlightSection, start:"top 80%", end:"top 20%", scrub:0.6}})`. Every descendant reads it with a `var(--night-t, 0)` fallback (so day-only windows are unaffected): the sun's opacity is `calc(1 - var(--night-t))`, the moon's opacity is `var(--night-t)`, a scattered field of 14 star dots fades in at `calc(var(--night-t) * 0.8)`, a full-bleed dark overlay above the mountains layer darkens to `calc(var(--night-t) * 0.55)`, and the night town plate (same silhouette, windows lit) fades in above the day plate at `var(--night-t)` — the town's windows come on because the whole lit version of the town resolves into place. The Footer window reuses the identical markup with `--night-t` fixed to `1` (no scrub — by the terminus it is simply night).

## Section 1: Hero

Full-viewport window view behind the copy (`z-index` above the scenery: brand mark top-left, "MERIDIAN LOCAL"). Eyebrow "A SCENIC RAIL CAMPAIGN"; H1 "Four landscapes. / One window seat." (`clamp(2rem,4.8vw,3.5rem)`, weight 800, text-shadow for legibility over the sky); sub-copy about the branch line running at "walking-window speed"; a "↓ Take your seat" hint bouncing `translateY(0→6px)` over 2.2s. Top-right: a monospace digital display panel (dark rounded chip, `border:1px solid rgba(144,164,174,0.35)`) reading eyebrow "NEXT STOP" / line "MERIDIAN YARD — DEP 07:12". Two hanging straps (thin line + open ring, SVG) sway near the top of the frame.

## Section 2: The Line

Eyebrow "THE LINE", H2 "Four stops, one afternoon." Below, a horizontal dashed track line with a glowing dot marker that slides `left:0% → 100%` (`ease:"none", scrollTrigger:{trigger:section, start:"top 75%", end:"bottom 25%", scrub:0.5}`) across 4 stop entries laid out in a row (2 cols under 860px, 1 col under 560px): **Meridian Yard** ("Departs the coastal yard at dawn"), **Fernhollow** ("Orchard town, market platform"), **Harborlight** ("Harbor lights after dusk"), **Twilight Terminus** ("Journey's end, city lights").

## Section 3: Fernhollow

Full-viewport window (index 1, day palette), copy pinned to the bottom-left (flex column, `justify-content:flex-end`): eyebrow "NOW ARRIVING", H2 "Fernhollow.", a line about orchards and a market square at the platform. Top-right display: "NOW ARRIVING" / "FERNHOLLOW — PLATFORM 2". As the section scrolls into range the throttle (and therefore the whole scenery) audibly-in-motion-terms slows to a stop and the "FERNHOLLOW" platform plaque resolves into view at the trackside.

## Section 4: Onboard

Two-column grid (stacks under 860px): left is eyebrow "ONBOARD", H2 "Built for the view.", and a 4-item amenity list (deep-recline scenic seats, a café cart, fold-down trays, one quiet car). Right is a static interior window card (`aspect-ratio:4/3`, no live scenery — the AI-generated onboard-blur plate, `object-fit:cover`, over a `#2C3A44→#1B242B` gradient fallback) with 3 hanging straps (the largest strap cluster on the page) and a seat-back tray digital display: "EN ROUTE" / "HARBORLIGHT — 24 MIN". This is the dedicated showcase of signature ③'s interior vocabulary (straps, window frame, digital display) without a station event competing for attention.

## Section 5: Harborlight

Full-viewport window (index 2, day→night scrubbed as described above), copy bottom-left: eyebrow "NOW ARRIVING · EVENING", H2 "Harborlight, after dark.", a line about the town's windows coming on one street at a time. Display: "NOW ARRIVING" / "HARBORLIGHT — PLATFORM 1". Same approach/depart throttle mechanic as Fernhollow, now paired with the night transition, so the train visibly slows into a darkening, light-speckled town.

## Section 6: Footer

Full-viewport night window (index 3, fixed night palette, `--night-t:1`), centered content: eyebrow "TWILIGHT TERMINUS", H2 "Your next window seat is waiting.", copy inviting the reader to reserve a window seat, a pill CTA "Book the Local" (background accent, `border-radius:9999px`, hover lifts `translateY(-4px)` over 0.3s `cubic-bezier(0.22,1,0.36,1)` — a simple lift, never a magnetic cursor-follow), fine print "Runs daily · Reserved window seats on every car". Top-right display: "ARRIVED" / "TWILIGHT TERMINUS — 21:06". The footer's own throttle zone (`1→0`, `power2.out`, `start:"top 85%", end:"center center", scrub:0.5`) brings the whole page's journey to its final, permanent stop — no departure tween follows it.

## Scroll animations

Every heading/eyebrow/paragraph/list-item/CTA across all 6 sections is marked `[data-reveal]` and shares one "resolve into focus" entrance — a motion-blur streak clearing, not a plain y+opacity fade: at rest `opacity:0, x:-40px, skewX:-8deg, filter:blur(6px)`; a `ScrollTrigger` (`start:"top 78%", toggleActions:"play none none reverse"`) tweens to `opacity:1, x:0, skewX:0deg, filter:blur(0px)` over 0.8s `power3.out`, staggered 0.09s, and reverses over 0.4s `power2.in` on leave-back. Hanging straps sway independently of the throttle system: `rotate(-Ndeg ↔ Ndeg)` where N is `[4, 5, 3.5]` degrees per strap, durations `[3.2, 3.8, 2.9]s` ease-in-out infinite, delays `[0, 0.6, 1.2]s` — always alive, even during a full station stop.

## Performance & accessibility

Register ScrollTrigger once; build every tween/ScrollTrigger inside one `gsap.context()` scoped to the root so it reverts cleanly on teardown, and remove the `gsap.ticker` callback on unmount. The ticker only ever writes `patternTransform` on 16 SVG `<pattern>` elements and `opacity`/`transform` on 4 platform divs per frame — no React re-render, no layout thrash (pattern re-tiling is paint-only, not reflow), no canvas or WebGL anywhere on the page. Respect `prefers-reduced-motion:reduce`: skip the ticker entirely (scenery holds on a single static frame instead of animating), skip every throttle/night/route-marker scrub tween (set `--night-t:1` and a fixed `0.75` platform opacity directly via CSS instead), and resolve every `[data-reveal]` target straight to its visible state with no blur/skew. Disable the strap-sway and glass-glint keyframes via a matching `@media (prefers-reduced-motion:reduce)` rule and remove the CTA's hover lift. On teardown call `ctx.revert()` and disconnect the `ResizeObserver` attached to the scroll container.

## 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": "train-view",
  "brand": "MERIDIAN LOCAL",
  "accent": "#47645a",
  "title": "Your afternoon by the window",
  "intro": "Four landscapes, one unhurried journey. Choose the carriage that fits the way you like to travel.",
  "facts": [
    [
      "Route",
      "Fernhollow → Harborlight → Twilight Terminus"
    ],
    [
      "On board",
      "Reserved window seats, fold-down trays and a café cart"
    ],
    [
      "Quiet space",
      "Phone-free quiet car at the rear"
    ],
    [
      "Arrival",
      "21:06 at Twilight Terminus · sample timetable"
    ]
  ],
  "groups": [
    {
      "label": "Carriage",
      "options": [
        {
          "title": "Scenic carriage · £36",
          "detail": "Reserved window seat, standard reclining seat and access to the café cart."
        },
        {
          "title": "Quiet carriage · £36",
          "detail": "The same scenic seat in a phone-free carriage. Headphones required for audio."
        }
      ]
    }
  ],
  "date": true,
  "notes": [
    "Indicative one-way adult fare. This planning tool does not hold a seat. A real booking would confirm route origin, departure time, accessibility, luggage rules and fare conditions."
  ]
}

- 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.