MOTION PALETTE
← すべてのデモ

Tilt Gallery

InteractionJS / CSS 3D / SVG / GSAP

フルスクリーンで開く ↗

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

再現プロンプト

架空のデザインエージェンシー「Amaranth Studio」の縦長1ページサイト。見出しの文字群がカーソル近接で散開し、SVGの糸がカーソルの引力でたわみ、背景の環境光が追従し、スクロール速度でページ全体が傾く——全要素がカーソルに反応する1本のrAFループで駆動する。 配色・構成・動きの数値をまとめた指示書です。 お使いのAIに貼り、素材やお店の情報を伝えて実装してください。仕上がりは利用するAIと素材によって変わります。

Build a single self-contained HTML file (inline CSS + inline vanilla JS, no framework; CDN allowed only for GSAP + its ScrollTrigger plugin; apart from the six local Works artworks — see "## Imagery" — every visual is CSS/SVG, no external image hosts) for "Amaranth Studio" — a fictional boutique design & motion agency. This is a complete, vertically scrolling one-page site (window scroll is fine; a container-scroll constraint only applies to the embedded-iframe version of this demo). Five sections, one motion language throughout — EVERY element answers the cursor, not just the hero: Hero → Thread → Works → Field (capabilities) → Contact.

## Art direction
Background #14090F (near-black rose-charcoal) everywhere. Foreground text #F5F5F5, muted text rgba(245,245,245,0.6–0.7), accent #F06292 (rose pink) for CTAs, eyebrows, thread strokes and glows. Two fixed background light layers sit behind all scrolling content: (1) an idle radial-gradient blob (rgba(240,98,146,0.18), transparent 70%, ~50% viewport size, offset -25% inset) drifting via a 22s ease-in-out infinite keyframe (translate ±4%/±3%, scale 1↔1.08), disabled under "prefers-reduced-motion: reduce"; (2) a 640px "environment light" circle (radial-gradient rgba(240,98,146,0.55)→rgba(240,98,146,0.08) at 45%→transparent at 70%, blend-mode screen) whose center is eased every animation frame toward the live cursor position at lerp 0.045 ('pos += (cursor - pos) * 0.045'), opacity 0.9 while the pointer is active and 0 otherwise — this shared, cursor-following ambient light is identical across every section. Section padding is generous (5rem 1.5rem, min 60–100vh). Headings are bold sans-serif, fluid clamp() sizing (hero h1 clamp(2.2rem,6.4vw,4.6rem); section h2 clamp(1.7rem,4vw,2.6rem)); eyebrows 0.72rem uppercase, letter-spacing 0.28em, #F06292. Buttons are pills (radius 9999px), background #F06292, text #14090F, weight 600. Only this page uses magnetic buttons.

## Section 1: Hero
Full-viewport, centered, text-center. Eyebrow "Design & Motion Studio — Est. 2018". H1 "We build interfaces that lean toward you." — every character is its own inline-block span (a visually-hidden sibling span carries the plain string for screen readers). On load, each character starts scattered (x ±110px, y ±70px offset −30px, rotate ±45deg, opacity 0) and animates to identity (duration 1.1s, power3.out, per-character stagger 0.012s in random order, 0.15s delay) — the letters converge into place, foreshadowing the cursor-reactive behavior that owns them next. Supporting copy "Amaranth designs interfaces where every character, curve, and control senses the cursor and answers back — tilt, gravity, and light, tuned frame by frame." fades in once (opacity 0→1, y 24px→0, 0.9s power3.out, 0.08s stagger, 0.15s delay) with a magnetic pill CTA "Start a project →" that smooth-scrolls to Contact, and a "↓ Scroll" hint bouncing via a 2.2s ease-in-out infinite keyframe (translateY 0↔6px, opacity 0.55↔1).

## Section 2: Thread
Eyebrow "Under The Surface". H2 "One line, pulled taut by attention." (also a per-character cursor field). Intro: "Everything below is one continuous thread — the same tension that answers your cursor also carries how we work." Three stacked rows, each clamp(56px,8vw,90px) tall, each holding one live SVG quadratic-bezier path (stroke #F06292, width 2px, round caps, opacity 0.75) resting as a straight line: 'M0 {h/2} Q {w/2} {h/2} {w} {h/2}'. Every frame, per row: add an idle vertical sway to the baseline, 'idleY = sin(time*0.6 + rowIndex*1.7) * 4px', if the cursor's vertical distance from the (idled) baseline is under 230px, set 'pull = 1 - dist/230' and target the control point's x toward the cursor's local x (clamped to row width, lerped by pull) and y toward 'baseline + idleY + pull * clamp(cursorLocalY - baseline, -130, 130)'; otherwise the target stays the idle baseline. Move the control point each frame with underdamped spring physics: 'v = (v + (target - pos) * 0.08) * 0.82; pos += v' (k=0.08, damping=0.82) — taut while the cursor is near, elastic overshoot-then-settle once it leaves. Each row is preceded by a bold 1.05rem label and one 0.88rem muted copy line — no numerals: "Immerse" — "We spend the first week inside your product — sitting in on support calls and sales demos until we can predict your users' next move."; "Craft" — "Design systems and motion language get built in parallel, then stress-tested against real content and edge cases from day one."; "Deploy" — "We ship inside your own codebase, document every component, and stay on call through the first release cycle." On scroll-in (section top crosses 75% viewport), rows reveal via scaleY 0→1 (origin 50% 50%) + opacity 0→1, 0.7s power2.out, 0.12s stagger — a "string snapping taut" reveal, reversing 0.3s power2.in on scroll-back.

## Section 3: Works
Eyebrow "Selected Work", H2 "Six studies in tactile interface design.", one-line intro. A responsive grid ('repeat(auto-fit, minmax(min(260px,100%),1fr))', gap 20px) of 6 abstract-art cards, 224px tall (256px at ≥640px), radius 20px, 1px border rgba(245,245,245,0.08), shadow "0 20px 60px rgba(0,0,0,0.45)". Each card: title (24–30px semibold #F5F5F5), one-line tagline (14px, rgba(245,245,245,0.7)), pill "View →" button — no numeric index label. A bottom-to-top gradient overlay (rgba(20,9,15,0) 40%→rgba(20,9,15,0.75) 100%) keeps text legible. Each card's face is a full-bleed local artwork image (work-prism.webp … work-ember.webp, see "## Imagery"): an absolutely-positioned '<img>' inside the pattern layer ('inset:0; width/height:100%; object-fit:cover; pointer-events:none', empty 'alt', 'loading="lazy"', 'decoding="async"', 'draggable=false'). Never size it past the card box: the card's preserve-3d context disables overflow clipping in Chromium, so an overscanned image would leak past the rounded corners — the artwork's outer 15% is crop-safe bleed and the 3:2 → card-aspect cover crop spends it, so the ±12° tilt + translateZ parallax never exposes an edge. Beneath the photo, keep a pre-load CSS-pattern fallback as the layer's background (titles in order): (1) "Prism" — conic-gradient(from 200deg, #F06292, #FFB199 30%, #7C6CFF 62%, #F06292 100%); (2) "Halo" — repeating-radial-gradient rings (#2A1030/#3D1B2E, 8px steps) + rose radial glow at 70% 75%; (3) "Flux" — repeating-linear-gradient diagonal stripes at 115deg (#2A0F1D/#3B1428, 10px steps) with a rose wash; (4) "Cinder" — three overlapping radial blobs (#F06292, #7C6CFF, #FF8A65) over #170A12; (5) "Drift" — a dot-grid radial pattern over #1B0E17→#2A1030; (6) "Ember" — a repeating-conic checker (#24101B/#2E1522, 90° steps) with a central rose glow — the opaque artwork simply covers the fallback once decoded.

## Imagery
Six local WebP artworks (960×640, 3:2, each under 50KB), one per Works card in order. Generate each with an AI image tool; every prompt shares the same style block — "abstract 3D render for a design-studio portfolio, soft studio lighting on dark rose-charcoal background, glossy and matte materials mixed, shallow depth of field, premium minimal art direction, edge-to-edge composition that continues past the frame, quiet right third and bottom of frame, no text, no letters, no UI labels, no logos, no watermark" — palette strictly base dark rose-charcoal #14090F family (#170A12/#1B0E17/#2A1030/#2A0F1D) with accents ONLY rose #F06292, violet #7C6CFF, peach #FFB199, orange #FF8A65 (no greens/cyans/yellows). Keep important detail inside the central 70% (outer 15% is crop-safe bleed), keep the right third and bottom dark and low-density so the white title + pink CTA stay legible, and light all six identically so they read as one studio's series, brightness ordered prism (brightest) → ember (darkest): (1) work-prism.webp — light refracting through a glass prism into rose/peach/violet beams, the one bright frame; (2) work-halo.webp — a glowing concentric-ring form floating in dark plum #2A1030 space, faint rose glow spilling from lower right; (3) work-flux.webp — ribbons/fluid streams flowing along one ~115° diagonal over deep maroon #2A0F1D, rose light trails; (4) work-cinder.webp — three luminous blobs (rose, violet, orange #FF8A65) colliding and fusing over #170A12; (5) work-drift.webp — a sparse field of tiny drifting luminous particles in a loose current over #1B0E17→#2A1030, white-to-rose low glow; (6) work-ember.webp — a slow-burning ember glowing rose-to-crimson at center, edges sinking into charcoal black, the darkest frame. 3D tilt + glare + magnetic CTA run on the same rAF loop (see below). On scroll, wrap each card in a perspective(900px) tile and reveal the TILE — never the tilting card element itself, to avoid fighting the live hover transform — from rotateX -45deg / y 36px / opacity 0 to identity, 0.9s power3.out, 0.08s stagger, at the section's top-80%-of-viewport line, reversing 0.3s power2.in on scroll-back.

## Section 4: Field (capabilities)
Eyebrow "Capabilities". H2 "A cursor away from everything we do." Copy: "Reach toward any of these and watch it lean back — the same responsiveness we build into your product." A flex-wrap cloud of 8 pill tags (Motion Systems, Interaction Design, Design Engineering, Brand Worlds, Prototyping, Micro-interactions, Design Ops, Sound Design; padding 0.6rem 1.15rem, 1px border rgba(245,245,245,0.14), background rgba(245,245,245,0.03), 0.85rem text). Every frame, for tags within 150px of the cursor: outward unit vector from cursor to tag center × 30px × (1 − dist/150) displacement, plus rotate up to ±10deg, eased at lerp 0.15 — tags dodge the cursor — the opposite polarity of the Works magnetism. On scroll-in (top 78% of viewport), tags scatter-in with alternating sign per index: x ∓24px / y 18px / rotate ∓12deg / opacity 0 → identity, 0.6s back.out(1.7), 0.045s stagger; per-frame repel writes pause while this one-shot tween runs.

## Section 5: Contact
Centered, min-height ~90vh, border-top 1px rgba(245,245,245,0.08). H2 "Let's make something worth stopping for." (same per-character cursor field as every heading). Copy: "Tell us about the project — timelines, budgets, and vague ideas all welcome." A large magnetic pill CTA "Get in touch ↗" → "opens the local planning dialog" (padding 1.1rem 2.6rem, 1.05rem). Footer meta: "© 2026 Amaranth Studio" · "Based in — everywhere" · "@amaranth.studio" (0.8rem, rgba(245,245,245,0.4), centered, 1.5rem gaps).

## Interaction & animation timeline (single requestAnimationFrame loop)
Every continuous effect (cursor, character fields, thread springs, tilt cards, magnets, tag repel, scroll-velocity skew) runs on ONE rAF loop. Handlers only write raw values (pointer x/y, active flag, scrollTop); the loop alone reads, eases, and writes to the DOM. (1) Heading character field: every h1/h2 is split into per-character spans; each frame, per character, within a 150px cursor radius apply falloff 't = (1 - dist/150)^1.4', displace 26px away from the cursor along the cursor→character angle, rotate up to 22deg toward the same side, eased at lerp 0.16; outside the radius the target is (0,0,0); skip writes once the delta is under 0.02. (2) Works tilt cards: hover computes normalized pointer nx, ny in [-1,1]; rotateY = nx×12deg, rotateX = -ny×12deg (perspective 900px on the wrapping tile), eased at lerp 0.12 via CSS custom properties (--rx, --ry, --lift, --gx, --gy, --glow); inner layers translateZ = lift × max (pattern 20px, title 52px, tagline 38px, CTA 64px, glare 88px); glare tracks the pointer at lerp 0.18. (3) Magnetic CTAs, two tiers: Works-card CTA — within 90px of the button center, displacement = (pointer − center) × 0.4 clamped to 18px, lerp 0.22, "transition: none"; hero/contact CTAs — same 0.4 pull and 0.22 lerp, 150px radius, 28px max. The instant the pointer exits the radius, the target snaps to (0,0) and the button switches to "transition: transform 500ms cubic-bezier(0.34,1.56,0.64,1)" for a one-shot elastic return. (4) Custom cursor: 6px dot tracks instantly, 28px ring lags at lerp 0.15 and scales to 1.9× (~53px, lerp 0.2) with a "View" label over any Works card; native cursor hidden ("cursor: none") while active. (5) Scroll-velocity skew: each frame, read scrollTop, take the delta from the previous frame, smooth at lerp 0.3, multiply by 0.045 and clamp to ±3deg, then ease the content column's skewY toward that value at lerp 0.2 — fast scrolling visibly leans the page, settling flat the instant scrolling stops. Keyboard: Works cards are focusable (tabindex 0); focus applies a fixed emphasis tilt (rotateX -8deg, rotateY 8deg, lift 1, glare 50%/35% opacity 0.6) through the same state machine, plus a 2px #F06292 focus outline (offset 4px). Touch: disable every continuous pointer effect (character field, thread pull, tilt, cursor, magnets, tag repel) and render cards flat with a discrete "scale(0.97)" tap class; scroll reveals and scroll-velocity skew still run.

## Scroll animations (shared rules)
Use one scroll-linked library (GSAP ScrollTrigger), scoped to the page's own scroll container. (1) Hero character field plays once on load, not scroll-linked; hero eyebrow/copy/CTA/hint play a single opacity/y fade on load only — the only plain fade on the page, used once. (2) Every other section heading reveals via a shared skew-wipe: opacity 0→1, y 28px→0, skewY 6deg→0deg, 0.85s power3.out, at the section's top-80%-of-viewport line, reversing on scroll-back — deliberately not a plain fade, it echoes the page-wide velocity-skew signature. (3) Thread rows scaleY-reveal, (4) Works cards rotateX-tilt-in reveal, (5) Field tags scatter-in reveal — all described above. Under "prefers-reduced-motion: reduce": skip the rAF loop, custom cursor, both ambient light layers, and scroll-velocity skew entirely; render every character field and thread path resting with no motion; every scroll reveal applies its final state instantly, keeping only a 200ms border-color/box-shadow hover/focus transition as minimum feedback.

## Performance & accessibility
Cap the interactive layer to ~40 tracked nodes/frame (~100 heading characters, 3 thread rows, 6 Works cards + CTAs, 8 tags, 2 standalone CTAs): ~15 batched getBoundingClientRect() reads (before any writes) plus under 150 transform/attribute writes at 60fps on a mid-range laptop. Never write layout-affecting CSS inside the loop — only transform/opacity/CSS custom properties/SVG 'd', all GPU-compositable; mark every animated element "will-change: transform". Cancel the rAF handle, revert the GSAP context and ScrollTrigger instances, disconnect ResizeObservers, and remove pointer listeners on teardown. Character spans carry a visually-hidden sibling with the real heading text so screen readers announce words, not letters; cards and CTAs stay real, labeled, keyboard-focusable controls; tags and thread rows are decorative motion over real, readable text.

## 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": "tilt-gallery",
  "brand": "AMARANTH STUDIO",
  "accent": "#8d3354",
  "title": "Selected study",
  "intro": "An interface study exploring the relationship between touch, material and light.",
  "facts": [
    [
      "Studio role",
      "Art direction, interaction design and frontend prototype"
    ],
    [
      "Scope",
      "Responsive composition, keyboard interaction and motion alternatives"
    ],
    [
      "Handover",
      "Working prototype, interaction notes and asset inventory"
    ]
  ],
  "notes": [
    "Independent concept study. No client endorsement or measured business outcome is claimed.",
    "The same visual hierarchy remains legible when tilt and depth motion are disabled."
  ]
}

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




Each of the six work buttons opens its own title, tagline and artwork, study scope and handover details. Keep all work buttons reachable by keyboard. The separate contact CTA collects a project brief.