Generate two pizza shots in Google Flow. Turn them into a video. Slice the video into frames. Drop the frames into Antigravity. Let Claude do the rest.
4 tools3 prompts~10 min
01
Google Flow
Two pizza images + one cinematic video.
02
EZGIF
Slice the video into 30-fps PNG frames.
03
Antigravity
Project workspace where Claude builds the site.
04
Claude Opus
Writes the Next.js scroll-scrub site for you.
1
Image one — the hero pizza
Generate the floating pizza.
Open Google Flow. Stay in image mode. Paste the prompt below and generate. Pick the cleanest shot — sharp focus, true black background, no plate or hands creeping in. Save it as pizza-1.png.
Prompt 1 · Image
Ultra-realistic premium pizza centered on a deep black background, cinematic studio lighting, top-down slight angle view.
Perfectly baked artisan pizza with melted cheese, pepperoni slices, fresh basil leaves, glossy texture, crispy crust with charred edges.
Soft diffused lighting from top-left, subtle shadows, no environment, no table, no plate — floating product shot.
Luxury food photography style, hyper-detailed, sharp focus, shallow depth of field, octane render, unreal engine quality, 8K resolution.
No text, no watermark, no hands, no props, no background elements.
Tip · Generate 3–4 variations and pick the one with the cleanest edges. The animation later only looks as good as your source image.
2
Image two — the exploded view
Generate the layered pizza.
Same place, new prompt. This one shows the pizza split into floating layers — that's what gives the final video its dramatic separation moment. Save the best result as pizza-2.png.
Prompt 2 · Image
A hyper-realistic exploded view of a premium pizza, centered in frame on a deep black background.
The pizza is separated into multiple floating layers in perfect vertical alignment:
- top layer with melted cheese, pepperoni, and toppings
- middle layer showing stretchy cheese strands pulling apart
- bottom crust layer resting slightly below
All layers are evenly spaced and symmetrically aligned, floating in the air like a high-end product breakdown.
Cheese stretch is natural and detailed, forming thin elastic strands between layers.
Small crumbs and tiny particles subtly falling downward.
No dust, clean environment.
Lighting is soft cinematic studio lighting from top-left with subtle highlights and shadows.
Materials are ultra-realistic:
- crispy golden crust
- glossy melted cheese
- rich tomato sauce
- detailed toppings with slight oil shine
Shallow depth of field, sharp focus on pizza layers.
Modern luxury food photography style.
4K ultra-detailed render, octane render quality, unreal engine quality.
No text, no watermark, no plate, no table, no background objects.
Minimal composition, premium commercial aesthetic.
3
Animate it
Switch to video mode & generate.
In Google Flow, switch to video mode. Upload both images you just made — the whole pizza becomes the start frame, the exploded version becomes the climax. Paste the prompt below, generate, and download your favorite take.
Prompt 3 · Video
A hyper-realistic cinematic food animation of a premium pizza floating in a dark studio environment. fully of black background only.
Camera is completely static, locked-off shot, no movement, no zoom.
At the start, the frame is empty.
A fully detailed pizza enters from above, slowly falling down vertically with natural motion.
As the pizza reaches its final position, it gently settles as if landing on an invisible surface.
At the moment of impact, reacting realistically to the downward force, the pizza separates.
The pizza remains perfectly centered, with melted cheese, crispy crust, and detailed toppings (pepperoni, vegetables, herbs).
Background is deep black, no environment, no props.
Motion is smooth, physically accurate, no jitter, no distortion.
Ultra-realistic food rendering, shallow depth of field, 4K cinematic quality.
No text, no watermark, no additional objects.
Tip · Static camera matters. If the camera moves, your scroll-scrub will look shaky. Keep a true black background — that's how the page can blend the frames into the dark layout seamlessly.
4
Slice into frames
Search "EZGIF video to JPG".
Upload the video you just downloaded.
Set frame rate to 30 FPS.
Hit convert, then download the result as a ZIP.
Extract the folder somewhere you'll remember.
You'll end up with a folder of sequentially numbered images — frame_001.jpg, frame_002.jpg, and so on. That's the raw material for the scroll animation.
Tip · A 4-second clip at 30 fps gives you 120 frames — plenty for a smooth scrub. Way more than that and your page gets heavy. Way less and the animation feels choppy.
5
Hand it to Claude
Open the folder in Antigravity.
Open Antigravity, point it at the extracted frames folder, and connect Claude (Opus is best for this). Then paste the prompt below into the chat. Claude will build a Next.js site that scrubs through your frames as the user scrolls — exactly like the video reel.
Prompt 4 · Claude in Antigravity
Build a Next.js 14 (App Router) editorial marketing site for an artisanal Neapolitan pizzeria called PIOZZA, in TypeScript and Tailwind CSS. Target a "luxury restaurant magazine" aesthetic — restraint, considered typography, scroll-driven motion. Reference points: Carbone, Eleven Madison Park, Aesop product pages, Apple product pages. The site should feel cohesive and editorial, not template-y.
═══════════════════════════════════════════════════════════════════
ASSETS — frame sequence
═══════════════════════════════════════════════════════════════════
I have a folder of pizza-animation JPG frames at:
[PUT THE ABSOLUTE OR RELATIVE PATH TO YOUR FRAMES FOLDER HERE]
The frames may be named arbitrarily (e.g. ezgif-frame-001.jpg, frame_001.png, pizza_0001.webp). They form a sequential animation when ordered.
Before writing any code:
1. Run `ls` on that folder and report: total count, the first 3 filenames, the last 3 filenames, and `du -sh` total size.
2. Use `file` or PowerShell System.Drawing (on Windows) to verify the dimensions of frame 1.
3. Copy + rename them into `public/frames/` with zero-padded 4-digit names: `frame_0001.<ext>` … `frame_NNNN.<ext>`. Use the same extension as the source. CRITICAL: when using bash printf, run the source number through `$((10#$num))` first — plain `printf "%04d" "008"` interprets the leading zero as octal and silently drops files.
4. Use the discovered count as `TOTAL_FRAMES` constant — never hardcode a number.
5. If total size > 30 MB, warn the user and recommend optimization (cwebp / pngquant) before proceeding.
═══════════════════════════════════════════════════════════════════
DESIGN TOKENS
═══════════════════════════════════════════════════════════════════
Colors (Tailwind theme.extend.colors):
bg #000000 ← PURE BLACK — required so the canvas edges blend invisibly
ink #f7f1e8
red #ff3a26
gold #ffb627
hairline rgba(247,241,232,0.12)
Fonts via next/font/google with CSS variables:
Bodoni Moda (display, italic emphasis) → --font-bodoni
Inter (body) → --font-inter
JetBrains Mono (eyebrows, labels) → --font-mono
Global treatment:
- html { scroll-behavior: smooth }, body { overflow-x: hidden }
- SVG turbulence film-grain overlay, position fixed, mix-blend-mode: overlay, opacity 0.06
- Custom selection color (red bg, dark text)
- Respect `prefers-reduced-motion: reduce` everywhere — disable all keyframe animations and force [data-reveal] to its end state
- Preload first frame: <link rel="preload" as="image" href="/frames/frame_0001.<ext>"> in the document head
═══════════════════════════════════════════════════════════════════
SECTION 01 — HERO (frame-sequence canvas scrubber)
═══════════════════════════════════════════════════════════════════
Structure:
<section className="hero relative h-[500vh]">
<div className="sticky top-0 flex h-screen w-full flex-col overflow-hidden bg-black pt-16">
...
Vertical flex layout inside the sticky container:
1. Eyebrow + headline + subhead text block (centered, top)
2. flex-1 stage containing the canvas
3. Absolute bottom credits row
Canvas behavior — implement EXACTLY this:
1. PRELOAD all TOTAL_FRAMES into an HTMLImageElement[] on mount. Track a `loaded` count. Mark `ready=true` when count ≥ 80% of total. Until ready, show a centered loading veil with a hairline progress bar and "PREPARING… X%" mono text.
2. CANVAS RENDERING:
- Canvas absolutely positioned to fill its wrapper (h-full w-full)
- DPR-aware: canvas.width = clientWidth × min(devicePixelRatio, 2); same for height; ctx.scale handled implicitly via the bigger backing store
- Apply via inline style: `filter: contrast(1.18) brightness(0.95) saturate(1.15)` and `mask-image: radial-gradient(ellipse 50% 60% at 50% 50%, #000 30%, rgba(0,0,0,0.85) 50%, rgba(0,0,0,0.45) 70%, transparent 92%)` (with -webkit- prefix). The feathered radial mask makes the canvas dissolve seamlessly into the pure-black page bg — no rectangular boundary visible.
- Drawing math: `scale = Math.min(cw/img.width, ch/img.height) * 0.9` (CONTAIN with 10% breathing margin so the full source frame is always visible AND has space above/below — never cropped).
3. SCROLL HANDLER:
- Single passive scroll listener with rAF ticking flag — never call draw more than once per animation frame
- Compute progress: `p = clamp01(-section.getBoundingClientRect().top / (section.offsetHeight - innerHeight))`
- frame index = `Math.round(p * (TOTAL_FRAMES - 1))`
- Only redraw if frame index changed
- Mutate refs/styles directly for overlay updates — DO NOT trigger React re-renders on scroll
4. CONTINUOUS rAF LOOP (separate from scroll handler) drives:
- Idle bob on canvas wrap: `sin(t * 1.1) * 5px` translateY
- Cursor parallax on the stage element: rotateX up to ±5°, rotateY up to ±6°, translateX up to 10px — ease toward target with 0.08 lerp
- Cursor-follow spotlight: a blurred red radial div that translate3d-tracks the mouse, mix-blend-mode: screen, fades in/out with `.is-active` class
- Halo bloom behind canvas: `1 + sin(t * 0.7) * 0.04` scale pulse
- Floor shadow: elliptical blurred dark gradient that contracts vertically as the bob lifts
- Two ambient color orbs (red top-left, gold bottom-right) that parallax-drift on scroll (translateY = p × ±constant)
- Subtle scroll-driven canvas scale: `1 + p × 0.08`
5. RESIZE: re-set canvas backing-store size and redraw the current frame.
6. CLEANUP: remove all listeners and cancel rAF on unmount.
Hero overlays (each driven by p in the same rAF callback that draws):
- Eyebrow "— A SLICE OF NAPLES —" mono, tracking-[0.3em] sm:tracking-[0.5em], fades out 60–80% scroll
- Headline italic Bodoni "Taste the [Tradition.]" with red on "Tradition." — text-4xl sm:text-6xl md:text-7xl lg:text-8xl, parallax-rises and slightly shrinks as p grows
- Subhead "900° wood-fired. 60 seconds. Centuries of devotion." — lifts and fades in last 25%
- Scroll cue: bottom-centered, "SCROLL TO TASTE" + animated pulsing vertical hairline; fades out by 20% scroll
- Hidden on mobile (md:block), visible on desktop:
- Vertical side-rails (writing-mode: vertical-rl + rotate(180deg)) reading "PIOZZA · NAPOLI · 1962 · ARTIGIANO" left, "FOR. SCROLL — TASTE — TRADITION — ETERNAL" right
- Top-corner micro-labels: "(01) — HERO" left, "NEAPOLITAN / WOOD-FIRED" right
- Bottom credits row: "FRESCO / DAILY · NO PRESERVATIVES" left, scroll cue center, "01 / 04 — SECTIONS" right
Page entrance choreography (CSS keyframes with stagger via animationDelay):
- 0.2s eyebrow fades up
- 0.45s headline fades up
- 0.7s subhead fades up
- 0.9s canvas stage fades in
- 1.1s bottom credits row fades
- 1.2–1.4s side rails slide in
All gated behind prefers-reduced-motion.
═══════════════════════════════════════════════════════════════════
SECTIONS BELOW THE HERO (editorial layout)
═══════════════════════════════════════════════════════════════════
Every section follows the same 12-column grid pattern:
- Left column (md:col-span-3): metadata rail with section number "(02)", section label "PROLOGUE" in mono caps, optional editorial-rule horizontal line
- Right column (md:col-span-9): main content
- Vertical rhythm: py-20 md:py-32 (mobile-friendly)
- Section dividers: border-t border-hairline, no decorative dividers
- Numbered (01)–(07) across the page
Section order:
Nav → Hero (01) → Prologue (02) → Menu (03) → Stats (04) → Quote (05) → Features (06) → FinalCTA (07) → Footer
(02) PROLOGUE — drop-cap editorial paragraph
- Left rail: (02) / PROLOGUE / "Naples, 1962."
- Right: italic Bodoni paragraph with red drop cap on first letter (CSS :first-letter, font-size 5.5rem, float left)
- Below paragraph: 4-up grid of ingredient credits "Dough / 12-day cold ferment", "Tomato / San Marzano D.O.P.", "Cheese / Mozzarella di Bufala", "Fire / Quercia oak, 900°F"
- Two FloatingPizza decorative orbs in the gutters (parallax-drifting, 5–10% opacity)
(03) MENU — three-column horizontal grid
- Section header: massive italic "Three pies. / Zero compromise." (red on second line) with letter-by-letter SplitText reveal
- Body: grid-cols-1 sm:grid-cols-2 lg:grid-cols-3, three pie cards
- Each card:
• Square aspect-ratio pizza thumbnail (use a DIFFERENT frame index per pie to give visual variety — distribute across the sequence, e.g. frame 1, frame ~30% in, frame ~60% in)
• Subtle bottom gradient on image for label legibility
• Top-left corner badge "№ 01"
• Bottom-left Italian secondary name e.g. "— LA REGINA"
• Below image: hairline-bottomed row with Roman numeral (I/II/III, red) left + price (gold italic) right
• Pizza name in big italic Bodoni with SplitText char-stagger
• Description (compact, max-w-md)
• Ingredient list as inline mono "·" bullets
• "ADD TO CART" link with arrow icon, border-bottom underline
- Pies:
I — Margherita / La Regina — $18 — San Marzano · Bufala · Basil · EVOO
II — Diavola / Il Diavolo — $22 — Spicy Salami · Calabrian · Mozzarella · Sugo
III — Tartufo / Il Tesoro — $26 — Black Truffle · Mushroom · Fior di Latte · Truffle Oil
(04) STATS — by-the-numbers band
- Header: "A pizza is the sum of its [discipline]." with SplitText reveal
- 4-column band (2 on mobile) with hairline borders between cells:
900° / OVEN TEMP / "Quercia oak fire"
60s / BAKE TIME / "Stone hearth, no shortcuts"
12d / FERMENT / "Slow, cold, deliberate"
1962 / EST. / "Three generations"
- Each numeral animates count-up via IntersectionObserver (1.8s duration, ease-out cubic)
- Borders: use named booleans (isLastInRowMobile, isLastInRowDesktop, isLastRowMobile) — do NOT use long inline ternary chains; they break at breakpoints
(05) QUOTE — dedicated centerpiece
- min-h-[60vh] md:min-h-[80vh], vertically centered content
- Massive opening curly-quote glyph in red
- Italic Bodoni quote: "In the center of Naples, time is measured in dough rises and oven flares — [never minutes.]" (red emphasis), word-by-word SplitText reveal
- Attribution row: hairline rule + "NONNA LUCIA" with "PIZZAIOLA · NAPLES · 1962" beneath
- Big ghost FloatingPizza behind at ~10% opacity
(06) FEATURES — numbered craft grid
- Header: "Rooted in [centuries]. / Refined for today." with two SplitText lines, second offset by 400ms
- 2×2 grid (1 column mobile) of numbered features 01–04:
01 Lightning Fast — "Specialized e-scooters dispatch within five city blocks. Twenty minutes, door to door, never less than piping hot."
02 900° Wood-Fired — "Stone hearth fired with quercia oak. The crust leopards in seconds; the cheese settles in milliseconds."
03 100% Organic — "San Marzano tomatoes and Mozzarella di Bufala flown in weekly from the slopes of Vesuvius and the plains of Campania."
04 Heritage Craft — "Twelve days of cold fermentation. Three generations of dough wisdom. Passed by hand, never by paper."
- Hover: animated red underline grows from 0 to 12 width
- Same border-logic cleanup as Stats — named booleans, not ternary chains
(07) FINAL CTA — closing statement
- min-h-[80vh] md:min-h-[100vh]
- Big two-line italic Bodoni: "One bite. / [You're hooked.]" (red on second line) with SplitText 42ms char-stagger, second line offset 400ms
- Hours-of-operation copy below
- Primary pill: "ORDER YOUR SLICE →" red bg with red glow shadow
- Ghost link: "VIEW FULL MENU" with hairline-bottom border
- Multiple FloatingPizzas: huge ghost (700px, 16% opacity, slow spin) center, smaller corner accents
FOOTER
- Massive italic "piozza." wordmark at text-[18vw] md:text-[20vw] as design element (red period)
- Multi-column under it: newsletter form (email input with hairline-bottom underline + "SUBSCRIBE →" link), VISIT (address/hours), CONTACT (phone/email), FOLLOW (social icons via lucide-react)
- Bottom legal row: copyright / privacy/terms/accessibility / version
NAV (fixed top, backdrop-blur, border-b hairline)
- Wordmark left "PIOZZA." (red period) with small "EST. 1962" mono caption beside it
- Center links: "01 MENU", "02 STORY", "03 LOCAL", "04 CONTACT" — hidden on mobile (md:flex)
- Right: "ORDER NOW" red pill with pulsing presence-dot and ArrowUpRight icon that animates on hover
- On mobile: pill shows just "ORDER" (sm:inline NOW), tighter padding
CART BUBBLE — fixed bottom-right
- Pill (not circle): red bg with shopping-bag icon and "CART (0)" count
- Smaller on mobile (h-11 px-3), full size on sm+ (h-12 px-4)
═══════════════════════════════════════════════════════════════════
SHARED COMPONENTS
═══════════════════════════════════════════════════════════════════
<RevealController> — single client component that mounts a global IntersectionObserver. Any element with `data-reveal` fades up + slides in (translateY 36px → 0, opacity 0 → 1, 1s cubic-bezier(0.22, 1, 0.36, 1)). Optional `data-reveal-delay="1..4"` adds 100ms × N delay. Uses MutationObserver to catch nodes added later. Disable on prefers-reduced-motion.
<SplitText parts={Part[]} stagger={ms} delay={ms} by="char|word" className />
- Type Part = string | { text: string; className?: string }
- Renders each character (or word) wrapped in <span class="split-piece"><span class="split-inner">X</span></span>
- Outer span has overflow:hidden; inner translates from translateY(110%) → 0 over 1s when parent enters viewport (IntersectionObserver, threshold 0.3)
- Per-char transitionDelay set inline via inline style based on its sequential index
- Use this on every section headline and key statement — gives the "letters rising into view" effect
<FloatingPizza size={px} frame={n} opacity={n} spin={bool} parallax={n} className />
- Decorative pizza orb using a single frame as background-image
- circular border-radius, soft red drop-shadow
- CSS animation: float-y (8s ease-in-out bob)
- Optional spin-slow CSS animation (40s linear rotate)
- Optional parallax: passive scroll listener with rAF, computes `(rect.top + rect.height/2 - innerHeight/2) × parallax` and writes to a CSS var --parallax-y; element transform reads `translate3d(0, var(--parallax-y), 0)`. Composes with the float-y bob.
- Use throughout the page in section gutters at 5–10% opacity for atmospheric depth
<ScrollProgress> — fixed right-side rail (display: none below 1024px)
- Vertical list of 7 chapter labels with hairline-prefix bars
- As user scrolls, the active chapter highlights — hairline grows 18px → 32px and turns red, label brightens
- Active index = floor((scrollY / scrollMax) × CHAPTER_COUNT)
- Each row is a click-jump anchor
═══════════════════════════════════════════════════════════════════
RESPONSIVE
═══════════════════════════════════════════════════════════════════
The site must work cleanly from ~360px wide phones up through 4K displays.
Breakpoint matrix:
- Below sm (640px): mobile — side rails hidden, top-corner labels hidden, padding py-20, headlines text-4xl, eyebrow tracking 0.3em, Stats 2×2 grid, Menu 1 column
- sm: tablet portrait — Menu 2 columns, tighter padding still py-20
- md (768px+): desktop — side rails visible, padding py-32, Stats 1×4 row, headlines scale up
- lg (1024px+): full desktop — Menu 3 columns, ScrollProgress rail visible, headlines text-7xl/8xl
Borders in grids: never use long ternary chains for conditional border classes. Use named booleans (isLastInRowMobile / isLastInRowDesktop / isLastRowMobile) — they collapse cleanly at every breakpoint.
═══════════════════════════════════════════════════════════════════
PERFORMANCE NOTES
═══════════════════════════════════════════════════════════════════
- All scroll listeners must be rAF-throttled with a `ticking` flag
- Mutate refs/inline styles directly during scroll; never setState in scroll handlers
- Hero canvas DPR clamped to 2 to avoid runaway memory on retina
- Page bundle target: under 100 kB First Load JS (current real implementation is ~96 kB)
- Frames at 1920×1080, ~80 KB each — total ~20 MB for ~240 frames is acceptable. If your set is bigger, recommend the user downscale to ~1280×720 or convert to WebP.
═══════════════════════════════════════════════════════════════════
TONE / COPY GUIDELINES
═══════════════════════════════════════════════════════════════════
Editorial, sparing, sensory. No marketing-ese ("amazing", "best ever"). Use Italian secondary names (La Regina, Il Diavolo, Il Tesoro). Use restrained metric/material nouns (San Marzano D.O.P., quercia oak, fior di latte). Numbers should feel like specifications — 900°, 60s, 12d, 1962.
═══════════════════════════════════════════════════════════════════
WATCH OUT FOR (real pitfalls from building this)
═══════════════════════════════════════════════════════════════════
1. Bash printf with leading-zero filenames — wrap source numbers with $((10#$num)) or you'll silently lose ~35 frames between 080–099 to octal parsing.
2. The Footer must be marked `"use client"` if it contains a form with onSubmit handler — otherwise Next 14's static page generation will time out at "Failed to find ..." with three SIGTERM retries before giving up. Same applies to anything with onClick/onChange.
3. Tailwind CSS variables for fonts — set them on <html> via the next/font className composition (`${bodoni.variable} ${inter.variable} ${mono.variable}`), then reference as `var(--font-bodoni)` etc. in tailwind.config.ts fontFamily.
4. The frame sequence's source backdrop is near-black (~#181818) with grain. To make it disappear into the page, the page bg must be PURE BLACK (#000), and the canvas needs both a `contrast(1.18) brightness(0.95)` filter AND the feathered radial mask. Don't try to chroma-key it per-pixel — too expensive at scroll-tick rates.
5. Cover-fit (Math.max) crops the source — users perceive this as "cropped at top". Use contain (Math.min) × 0.9 to guarantee breathing margin and never crop.
6. Don't use mix-blend-mode: lighten on the canvas — it composites against everything beneath including ambient halo orbs, tinting the pizza unpredictably. Mask is more reliable.
7. Static page generation timeouts on Windows after a partial-failure can leave .next in an inconsistent state — `rm -rf .next` before any retry.
8. Pin Next.js to a patched 14.x line (currently ^14.2.34+) — earlier 14.2.x versions have a published security advisory.
═══════════════════════════════════════════════════════════════════
DELIVERABLE
═══════════════════════════════════════════════════════════════════
1. Show me the result of the asset audit (frame count, size, dimensions) BEFORE writing code.
2. Initialize the Next.js project (TypeScript, App Router, Tailwind), install lucide-react for icons.
3. Build all components and wire them into app/page.tsx.
4. Run `npm run build` and confirm production build passes with no errors.
5. Run `npm run dev` and confirm it starts cleanly.
6. Report final bundle size and any warnings.
Build it.
Tip · If the scroll feels jumpy, your frame count is too low. If the page is too heavy, it's too high. Sweet spot: 90–150 frames, total under 25 MB. Tell Claude "convert frames to WebP" if you cross that threshold.
6
Iterate
Ask Claude for changes.
Once it's running, just talk to Claude. Real examples that work:
"Make the headline italic and bigger."
"The scroll feels too fast — slow it down."
"Change the accent color from red to deep emerald."
"Add a fifth pizza to the menu — Quattro Formaggi, $24."
Iterate on one section at a time. Don't re-prompt from scratch — that's how good builds get worse.
Your site is ready.
If this helped, save it, share it, and tag me when you ship yours.