算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
cinematic-scrub-landing【设计与创意】Build a premium cinematic landing page with mouse-scrub video hero and brand-driven narrative-arc sections. Use whenever the user provides a hero video plus a product / subject / brand and wants a landing page, promo site, product showcase, marketing page, or storytelling site. Works for any language and any subject. The signature effect is mouse-dri…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Build a premium, brand-aware landing page where the hero video responds to the mouse and the rest of the page walks the viewer through a 5-beat narrative arc. The user provides a video, optionally a logo + product photo + brand context — this skill handles palette extraction, typography selection, RTL/LTR, scaffolding, the all-keyframes video re-encode, the mouse-scrub effect, the section library, and verification.
The reference implementation lives in https://github.com/hoodini/bamba — an Israeli RTL site for the snack "במבה" where a baby video tracks the cursor across the whole page while 4 distinct sections below (bright yellow recipe, cream nostalgia album, dark contemplative pricing, red action finale) walk the viewer through longing → joy → nostalgia → contemplation → action.

Every rule in this skill was extracted from that build. The bullets in the hard-rules section are ALL hard-won — they override generic "good code" defaults.
The hero stays on top. Below it, pick 4 sections from the library, ordered to follow the narrative arc. Each is fully opaque (no video bleed-through) and has its own brand identity:
| Beat | Type | Reference |
|---|---|---|
| 1 — Longing | Hero (mouse-scrub video) | ![]() |
| 2 — Joy | Sun — bright primary-accent gradient, floating motif SVGs, giant ghost typography, stat cards | ![]() |
| 3 — Nostalgia | Album — cream paper, dotted micro-pattern, polaroid bento with real product photo | ![]() |
| 4 — Contemplation | Roast — dark with radial accent glows, three pricing cards each with DIFFERENT visual treatment | ![]() |
| 5 — Action | Signal — full-bleed strong accent (red/orange/green), massive headline + CTA, glass-contained panel | ![]() |
For technical/SaaS brands, swap section types as needed (see the section library below) — but always end with a Signal section.
Drop assets into a clean working directory:
.mp4, .mov, .webm)Confirm with the user (briefly — propose defaults, move on):
Execute the workflow below in order. All phases are mandatory. Don't skip the ffmpeg re-encode — that's the magic that makes scrub feel buttery.
Verify before reporting done. Phase 9 has 12 checks. Run all of them.
PRODUCT_NAME: "<e.g. במבה / Bamba / Hope the Cheetah / Acme CRM>"
TAGLINE: "<the meta-message — e.g. 'it's not a snack, it's lifelong memories'>"
LANGUAGE: "<he | en | ar | es | fr | ja | ...>"
IS_RTL: "<true | false>" # he/ar/fa = true; everything else = false
HERO_VIDEO_FILE: "<exact filename in cwd — e.g. baby2.mp4>"
LOGO_FILE: "<exact filename — leave blank if none>"
PRODUCT_IMAGE_FILE: "<exact filename — leave blank if none>"
BRAND_CONTEXT: |
<2-4 sentences: era it evokes, who the audience is, the emotional core,
the visual identity (warm/cool/playful/serious). Drives palette + motifs.>
PALETTE: "<optional — comma-separated hexes if overriding>"
NARRATIVE_ARC: "<optional — 5 emotional beats; AI proposes if blank>"
The full text-template (with the same YAML block and prompt body) is in references/GOLDEN_PROMPT.md — paste it into Claude with the YAML filled in when you don't have direct skill-runtime access.
Before writing any code, output:
Extracted 5-color palette: background (deepest ink), cream (warm off-white text — NEVER #FFFFFF), accent (primary brand color), accent2 (secondary brand color), support (tertiary tone). Extract from the logo + product image if no PALETTE was given. The palette must be internally cohesive — all warm OR all cool, never mixed. Warm brands (food, nature, nostalgia, animals) → no blues, teals, mints, cyans. Cool brands (tech, finance, medical, sci-fi) → no warm tones except a tiny accent.
Typography trinity (display / body / handwritten-accent) for the language:
he: Rubik (display) + Assistant (body) + Suez One (accent)en: Anton (display) + Inter (body) + Caveat (accent)ar: Cairo (display) + Tajawal (body) + Reem Kufi (accent)es / fr / it / de: Anton or Bebas Neue (display) + Inter (body) + Caveat (accent)ja: Noto Sans JP weight 900 (display) + Noto Sans JP (body) + Yusei Magic (accent)display-black / body-clean / handwritten-script roles.Narrative arc — 5 emotional beats that map to BRAND_CONTEXT. Match each to a section type from the section library.
Then execute — do not ask for confirmation.
which ffmpeg # or: Get-Command ffmpeg on PowerShell
If absent, stop and tell the user to install (brew install ffmpeg / Windows: http://ffmpeg.org).
Browsers can only seek instantly to keyframes — standard video has ~1 keyframe per 250 frames, which makes mouse-scrub stutter horribly. This single command is the difference between premium and amateur:
ffmpeg -y -i <HERO_VIDEO_FILE> -g 1 -keyint_min 1 -c:v libx264 -preset slow -crf 20 -pix_fmt yuv420p -an hero.mp4
Verify the output: hero.mp4 should be 5-50 MB. If > 50 MB re-run with -crf 25; if > 80 MB with -crf 28. Strip audio (-an) — the hero is silent.
cp <LOGO_FILE> public/logo.<ext>
cp <PRODUCT_IMAGE_FILE> public/product.<ext> # if provided
npm create vite@latest site -- --template react-ts
cd site
npm install
npm install lucide-react
npm install -D tailwindcss@3 postcss autoprefixer
npx tailwindcss init -p
mkdir -p public
mv ../hero.mp4 public/hero.mp4
# Also move the logo + product image into public/
Replace tailwind.config.js entirely. Use the extracted palette and the language-appropriate fonts:
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
colors: {
background: '<ink/dark>',
cream: '<warm off-white — NEVER #FFFFFF>',
accent: '<primary brand color>',
accent2: '<secondary>',
support: '<tertiary>',
},
fontFamily: {
sans: ['<body font>', 'system-ui', 'sans-serif'],
display: ['<display font>', 'sans-serif'],
accent: ['"<handwritten font>"', 'cursive'],
},
},
},
plugins: [],
}
Replace entirely. Honor IS_RTL. Always include the favicon link pointing at the logo:
<!DOCTYPE html>
<html lang="<LANGUAGE>" dir="<rtl|ltr>">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<link rel="apple-touch-icon" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="<Google Fonts URL for the three chosen fonts>" rel="stylesheet">
<title><PRODUCT_NAME> — <TAGLINE></title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Replace entirely. Every utility below is load-bearing. Names assume the warm palette extracted earlier; for a cool brand swap "yellow"/"red" mentions in shadow tints for the actual accent colors.
Must include:
html { scroll-behavior: smooth; } + section[id] { scroll-margin-top: 90px; } so anchored sections don't land under the fixed nav.body background = background color (fallback if video fails). Cursor = crosshair. Default font = body sans..liquid-glass / .liquid-glass-strong — backdrop-blur + saturate + inset-light border + masked gradient edge highlight. Strong variant uses an inset glow tinted with accent..glass-pill — compact rounded pill with the same glass treatment for nav items and small badges..cta-primary — brand CTA: linear-gradient(135deg, accent-light, accent, accent-shade) + accent-color glow shadow that intensifies on hover + translateY(-1px) lift..brand-mark — small square/circle for tiny logo marks if no SVG is provided..grid-bg — subtle 1px grid with radial fade-mask, used under dark sections..reveal / .revealed — IntersectionObserver scroll-reveal: opacity 0 → 1 + translateY 28 → 0 over 0.9s with cubic-bezier(0.16, 1, 0.3, 1)..scrim-top / .scrim-bottom — gradient overlays for the hero only..sun-section — bright primary-accent gradient + subtle dotted texture (the "joy / opening the bag" beat)..album-section — warm off-white paper, radial accent tints + dotted micro-pattern, edge-mask fade..roast-section — dark with radial accent + accent2 glows in opposite corners + masked grid texture..signal-section — full-bleed strong accent (often red/orange/green) for the action finale..polaroid — white card, padding 14px 14px 44px 14px, soft layered shadow, hover lift to translateY(-6px) rotate(0). .polaroid-tape = yellow/accent tape strip absolutely positioned at top-center, rotated -4°, with dashed side hints..stamp — dashed-border pill in accent color, uppercase, letter-spacing 0.18em..ribbon — pricing/popular badge with clip-path triangle bottom, dark drop shadow..highlight-circle — hand-drawn-feel oval drawn via ::after: rotated 3px accent-color border around an inline <span>. Use it around the single most important word in big headings (one per heading max)..peanut-svg / .<motif>-svg with drop-shadow.prefers-reduced-motion: reduce — disable reveal animation + smooth scroll inside the media query.Architecture is non-negotiable. Get it right the first time:
import { useEffect, useRef, useState } from 'react';
import {
Menu, X, ArrowLeft, ArrowRight, ArrowDown, ArrowUp, MousePointer2,
// …plus 4-8 brand-thematic icons (Heart, Sparkles, Cookie, PawPrint, Cpu, Leaf, …)
} from 'lucide-react';
useReveal<T>() — IntersectionObserver hook returning a ref; adds revealed class on first intersect (threshold 0.15).GlassCard — wrapper with the reveal hook, optional strong prop, accepts className.Motif — a single decorative SVG component themed to BRAND_CONTEXT (peanut for food, paw for animals, circuit for tech, leaf for nature, droplet for beverage, bolt for energy). Floats at low opacity in 3-6 places per dark/bright section.ScrollToTopButton — fixed bottom-6 left-6 in RTL / bottom-6 right-6 in LTR. Appears when window.scrollY > window.innerHeight * 0.6. Smooth scrolls to top. Short language-appropriate label ("לראש" / "Top" / "أعلى" / "Inicio").const videoRef = useRef<HTMLVideoElement>(null);
const stateRef = useRef({ targetTime: 0, isSeeking: false });
useEffect(() => {
const video = videoRef.current;
if (!video) return;
const handleLoaded = () => {
const mid = video.duration / 2;
video.currentTime = mid;
stateRef.current.targetTime = mid;
};
const handleSeeked = () => {
const s = stateRef.current;
s.isSeeking = false;
if (Math.abs(s.targetTime - video.currentTime) > 0.01) {
s.isSeeking = true;
video.currentTime = s.targetTime;
}
};
const handleMouseMove = (e: MouseEvent) => {
const s = stateRef.current;
const d = video.duration;
if (!d || isNaN(d)) return;
const normalized = e.clientX / window.innerWidth;
// If subject moves "backwards" relative to cursor and feels wrong, optionally
// use `1 - (e.clientX / window.innerWidth)` instead — ASK the user first.
s.targetTime = Math.max(0, Math.min(d, normalized * d));
if (!s.isSeeking) { s.isSeeking = true; video.currentTime = s.targetTime; }
};
video.addEventListener('loadedmetadata', handleLoaded);
video.addEventListener('seeked', handleSeeked);
window.addEventListener('mousemove', handleMouseMove);
return () => {
video.removeEventListener('loadedmetadata', handleLoaded);
video.removeEventListener('seeked', handleSeeked);
window.removeEventListener('mousemove', handleMouseMove);
};
}, []);
The isSeeking guard is critical — without it, mousemove fires faster than the browser can decode and you get seek-queue overflow + visible stutter.
const navItems: { label: string; target: string }[] = [
{ label: '<demo>', target: 'top' },
{ label: '<recipe>', target: 'recipe' },
{ label: '<moments>', target: 'moments' },
{ label: '<family>', target: 'family' },
{ label: '<contact>', target: 'contact' },
];
const scrollToSection = (target: string) => {
setMenuOpen(false);
if (target === 'top') { window.scrollTo({ top: 0, behavior: 'smooth' }); return; }
document.getElementById(target)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
};
Wire onClick={() => scrollToSection(target)} on every nav item. The CTA button in the nav also calls scrollToSection('contact') (or whatever the final beat's id is).
return (
<div className="relative"> {/* NO bg-* class on this wrapper */}
<ScrollToTopButton />
<video ref={videoRef} src="/hero.mp4" muted playsInline preload="auto"
className="fixed inset-0 h-full w-full object-cover -z-20" />
<nav className="fixed top-0 inset-x-0 z-50 ...">{/* nav */}</nav>
{menuOpen && <div className="fixed top-24 inset-x-4 z-40 ...">{/* mobile menu */}</div>}
<section className="relative h-screen overflow-hidden">{/* HERO — video shows through */}</section>
<section id="recipe" className="sun-section ...">{/* OPAQUE — own identity */}</section>
<section id="moments" className="album-section ...">{/* OPAQUE */}</section>
<section id="family" className="roast-section ...">{/* OPAQUE */}</section>
<section id="contact" className="signal-section ...">{/* OPAQUE — the action finale */}</section>
<footer className="bg-background py-8 ...">{/* mini-footer */}</footer>
</div>
);
The outer <div> MUST NOT have a background color. Any bg-background / bg-cream / bg-white on the wrapper paints over the -z-20 video and the hero scrub stops working. Only body has bg-background as a fallback. This was the #1 mistake in the reference build. Catch it before you ship.
The hero is section 1. Then pick 4 more from this library, ordered to follow NARRATIVE_ARC. Do not repeat the same pattern twice. Each section is fully opaque with its own background — the video bleeds through the hero only.

Big primary-color gradient background. Floating motif SVGs at low opacity. Giant faded ghost number/word in a corner (e.g. founding year at 6% opacity, 260px). Headline panel: contrasting cream-tinted rounded card with strong shadow — heading inside, accent script word at -2° rotation as eyebrow, big display-black headline ending in a .highlight-circle word. Real product photo floats tilted next to the heading (6°, drop-shadow), hover-untwists. Three stat cards below in a row, each cream-rounded-[28px] with different slight rotations (-1.5°, 0.8°, -0.6°), each containing a massive display-black accent2 numeral (88-110px), a small label badge top-right, body text, footer attribution. Two large contrasting text panels at the bottom (one dark, one accent2-color) with thematic icons.

Cream paper background with subtle dotted micro-pattern (masked at edges). Giant faded handwritten word in accent2 at 4-6% opacity in a corner. Headline panel: pure-white rounded card with shadow. Beside it, a quote card: dark background, accent-colored quote icon, font-accent (handwritten) testimonial, attribution underline, slight -2° rotation. Body: polaroid bento grid — one large featured polaroid (md:col-span-2 md:row-span-2, -1.5° rotation) containing the PRODUCT_IMAGE inside a gradient frame with motif corner-tucks and a tag badge, plus 4 smaller polaroids each with a thematic icon-on-gradient image area, all with yellow/accent tape, all with handwritten captions, slightly different rotations.

Dark background with radial accent + accent2 glows in opposite corners, plus the masked grid texture. Floating motif SVGs at very low opacity. Headline panel: liquid-glass-strong rounded panel with accent-glow heading. Side label: glass-pill with thematic icon + one-line phrase. Body: three pricing cards with three DIFFERENT visual treatments (this variety is essential — never three identical cards):
.ribbon at top-right, accent2 huge price numeral, accent2 CTA with intense glow.cta-primary button.
Full-bleed strong accent color (red, orange, green — whatever the brand's "go" color is) with two large blurred orbs of accent2 in opposite corners and a giant centered accent2 oval blur behind the text. Floating motif SVGs at ~30% opacity. A single contained panel (dark-translucent backdrop-blur with accent tint) holding: .stamp badge eyebrow at -2°, MASSIVE responsive headline (60→148px) mixing display-black and a handwritten line in the middle, ending with a short final word. Subtitle below in 2 lines. Big primary CTA button (cream background, accent text) with a tiny pulsing dot indicator. Optional secondary outlined link. Logo flanked by horizontal dividers ("by ") at the bottom.
For tech / SaaS / B2B where one of the beats is "how it works". Dark background, alternating left/right two-column rows: a numbered eyebrow + heading + body on one side, a glass card with code/diagram/screenshot on the other. Strong vertical rhythm. Use in place of B or C when BRAND_CONTEXT is technical.
Always end with Type D (Signal) so the final beat is action. Even technical SaaS pages need this — replace the warm finale with a cool one (deep blue or violet) but keep the structure.
<div>. Only body gets bg-background. Anything else hides the video.position: fixed; -z-index: 20 — re-encoded with -g 1 -keyint_min 1.BRAND_CONTEXT establishes; end with action.liquid-glass-strong over dark sections, white/cream card over light sections, glass-pill for small cues. No text floats naked over busy textures.#FFFFFF for text — always use the project's cream value.h-9 / hero attribution h-12 / final-CTA attribution h-16 / mini-footer h-6.PRODUCT_IMAGE_FILE was provided, use it prominently — featured polaroid AND hero side accent AND/OR one pricing card.{ label, target } items, smooth-scroll to sections. scroll-margin-top: 90px on section[id] so they don't land under the nav.ArrowLeft. Cursor cue at bottom-LEFT. Mobile menu items right-aligned.ArrowRight. Cursor cue at bottom-RIGHT.index.html via <link rel="icon" type="image/svg+xml" href="/logo.svg" />. Add apple-touch-icon too.BRAND_CONTEXT (peanut, paw, circuit, leaf, droplet, bolt, snowflake). Floats at low opacity in 3-6 instances per dark/bright section.cta-primary class). Always with an arrow icon. Always with a tiny pulsing dot indicator if it's the primary primary action.npx tsc --noEmit after writing. It must pass with zero output. Then npm run dev and verify the server starts cleanly. Don't claim done until both pass.LANGUAGE. No mixing English boilerplate into a Hebrew page or vice versa. The only allowed non-LANGUAGE text is the brand mark itself, CSS font family names, and TypeScript/React identifiers.npx tsc --noEmit → zero output.npm run dev → Vite reports ready in <ms> with no errors.1 - (e.clientX / window.innerWidth).bg- class — find and remove it (hard rule #1).md.prefers-reduced-motion: reduce in devtools and reload. Scroll reveal should snap (no animation), smooth scroll should be instant.Report the dev URL it printed, the palette + arc you chose, and the section types you used.
The Bamba (במבה) site at https://github.com/hoodini/bamba — RTL Hebrew landing for an Israeli snack with all five sections, the mouse-scrub baby backdrop, clickable navbar, and real Osem brand assets. When something in this skill is ambiguous, that repo is ground truth.
The full text template (same content as this SKILL.md formatted for paste-into-Claude use) is in references/GOLDEN_PROMPT.md. Use it as a fallback when the skill runtime isn't available.
parallax-landing-page — scroll-driven, page itself locked, 5 text scenes crossfade as frames scrub. Use when the brief is "scroll scrubs the video and the page doesn't move".video-to-landing-page — Apple-style sticky-hero where scroll progresses the video frame through evenly-spaced JPEG extracts. Single hero with sections below.video-edit — captioning, transcript-based editing, social cuts. Not for landing pages.hyperframes — building HTML video compositions (the inverse direction — HTML → MP4).cinematic-scrub-landing) — mouse-driven scrub of a fixed full-page video backdrop, with a vertically-scrolling narrative arc of 4-5 opaque branded sections beneath. Pick this when the user wants the hero to respond to the cursor and the page below to tell a story.