{"slug": "prompt-i-used-to-build-a-3d-soda-landing-page-with-grok-4-5-in-one-shot", "title": "Prompt I used to build a 3D soda landing page with Grok 4.5 in one shot", "summary": "A developer used Grok 4.5 to build an interactive 3D soda landing page in a single shot. The page features a 3D soda can that tilts toward the cursor, floating berry models repelled by the pointer, and a flavor-switching animation with background color morphing and can spinning. The project uses Next.js, React, TypeScript, Tailwind CSS, GSAP, and Google's model-viewer.", "body_md": "| # Recreate this interactive 3D soda landing page in Next.js + React + TypeScript + Tailwind | |\n| # Design and 3D assets from getlayers.ai (Soda template). This prompt recreates the design using Grok 4.5. | |\n| You are an expert creative front-end developer. Build a **Next.js (App Router) project** with React, TypeScript, and Tailwind CSS that reproduces the design below exactly — same layout, visuals, motion, and interaction. | |\n| Use `pnpm` as package manager. Use GSAP for animations and Google's `@google/model-viewer` for 3D models. All 3D assets are hosted on a CDN (URLs below). The page should be a single full-viewport (no-scroll) hero landing page. | |\n| ## Project Setup | |\n| ``` bash | |\n| pnpm create next-app@latest soda-landing --typescript --tailwind --app --eslint | |\n| cd soda-landing | |\n| pnpm add gsap | |\n| pnpm add @google/model-viewer | |\n| ``` | |\n| ## What it is | |\n| A full-viewport (no-scroll) hero landing page for a fictional \"Diet Soda\" beverage. A dark radial gradient background (teal for the default \"Classic\" flavor, blue for the \"Zero Lime\" flavor) fills the screen. A large 3D soda can floats in the center, rendered with `<model-viewer>`, and **tilts toward the cursor in real time**. Around it, dozens of 3D cherry/leaf models float and drift with parallax, and are **repelled by the pointer** like a force field. Translucent PNG bubbles rise endlessly from the bottom. A glassmorphism header sits on top; a left column has a giant cursive headline + CTA + award badge; a right column has a two-card flavor carousel and a second headline. | |\n| Clicking a flavor card triggers a choreographed transition: the background color morphs, the can spins 720° with a motion blur and swaps its base-color texture at the peak, and all the berry models implode toward center, swap their model (cherry ↔ blueberry), then explode back out to new random positions. | |\n| ## Fonts | |\n| Add to `layout.tsx` via `next/font/google`: | |\n| - **Body**: `Inter` (weights 400, 500) | |\n| - **Headings**: `Galada` (cursive, weight 400) | |\n| - **Nav**: `Manrope` (weights 400, 700) | |\n| - **Logo/Headings alt**: `Outfit` (weights 400, 700, 900) | |\n| ## Color Palette | |\n| - Near-black: `#0a0a0a` | |\n| - Classic teal: inner `#0b8a78`, mid `#044e3b`, outer `#011411` | |\n| - Blue theme: inner `#0b4f8a`, mid `#04294e`, outer `#010c14` | |\n| - Pink accent: `#fbcfe8` | |\n| - Accent text on pink: `#011d17` | |\n| - Glass bg: `rgba(255, 255, 255, 0.05)` | |\n| - Glass border: `rgba(255, 255, 255, 0.1)` | |\n| - Muted text: `rgba(255, 255, 255, 0.7)` | |\n| ## Component Structure | |\n| ``` | |\n| app/ | |\n| layout.tsx — fonts, metadata, global styles | |\n| page.tsx — main page component | |\n| globals.css — custom CSS (gradients, animations, model-viewer styles) | |\n| components/ | |\n| Header.tsx — glassmorphism nav bar | |\n| HeroLeft.tsx — \"Pure Zero\" title, description, CTA, award badge | |\n| HeroRight.tsx — flavor carousel cards, \"Refreshingly Clean\" title | |\n| ProductViewer.tsx — center 3D can with model-viewer | |\n| FloatingBerries.tsx — foreground + background berry model-viewers | |\n| FloatingLeaves.tsx — background leaf model-viewers | |\n| BubblesContainer.tsx— rising bubble animation | |\n| FlavorCard.tsx — individual flavor card | |\n| ``` | |\n| ## Page Layout | |\n| The page does NOT scroll. `overflow: hidden; height: 100vh;` on body. | |\n| The body background is a radial gradient that transitions between teal (Classic) and blue (Zero Lime) themes using GSAP CSS variable animation. | |\n| The hero is a flex row spanning the viewport. Inside it (in DOM order): floating leaves (far background), left column, background berries, center product (3D can), foreground berries, right column. | |\n| ## Key Interactions | |\n| ### 1. Mouse cursor tilt on 3D can | |\n| Per animation frame, smoothly interpolate mouse position and update `model-viewer` camera orbit: | |\n| ``` | |\n| cameraOrbit = `${(currentMouse.x * 40) + switchSpin}deg ${90 + (currentMouse.y * 20)}deg 380%` | |\n| ``` | |\n| Mouse smoothing lerp factor: `0.05` | |\n| ### 2. Parallax on floating elements | |\n| - Foreground berries: `translate(mouse.x * 60px, mouse.y * 60px)` | |\n| - Background berries: `translate(mouse.x * -30px, mouse.y * -30px)` | |\n| - Leaves: `translate(mouse.x * -15px, mouse.y * -15px)` | |\n| ### 3. Berry pointer repulsion | |\n| Radius: `400px`, strength: `-80`, lerp: `0.1`, spin: `angle += 0.2 * speedMult`, `speedMult = 1 + force * 5`. Berry float amplitude: `15px` Y / `6deg`. Per-index durations: `[5, 7, 6, 8, 5.5, 6.5, 9, 11, 10]`. | |\n| ### 4. Flavor switch choreography | |\n| On card click: | |\n| 1. **Background morph**: GSAP animates CSS variables `--bg-inner`, `--bg-mid`, `--bg-outer` over `1.5s ease power2.inOut` | |\n| 2. **Can spin**: Phase 1 → `360deg`, `blur 15px`, `0.6s`, `power2.in`. At peak, swap texture. Phase 2 → `720deg`, `blur 0`, `1.5s`, `back.out(0.7)` | |\n| 3. **Berry implode/explode**: Implode to center `0.5s power2.in`, `scale 0.1 opacity 0`. Hold `0.3s`. Swap `.src` (cherry ↔ blueberry). Explode to random `±100px`, `0.9s back.out(1.5)` | |\n| ### 5. Rising bubbles | |\n| Spawn bubble `<img>` every `400ms`. Size: `10-30px`, opacity: `0.2-0.6`, rise duration: `4-10s`, drift `+30px` X, `360deg` rotation over `-110vh`. | |\n| ### 6. Floating leaves | |\n| Amplitude: `20px` Y, `15px` X, `15deg` rotation. Durations: `10 + i*2` seconds. | |\n| ## model-viewer Setup | |\n| Since `@google/model-viewer` is a web component, in Next.js you need to handle it as a client component. Create a wrapper: | |\n| ``` tsx | |\n| 'use client' | |\n| import '@google/model-viewer' | |\n| // Declare the custom element for TypeScript | |\n| declare global { | |\n| namespace JSX { | |\n| interface IntrinsicElements { | |\n| 'model-viewer': any | |\n| } | |\n| } | |\n| } | |\n| ``` | |\n| Center can settings: | |\n| - `camera-orbit=\"0deg 90deg 380%\"` | |\n| - `field-of-view=\"30deg\"` | |\n| - `exposure=\"1.5\"` | |\n| - `environment-image=\"neutral\"` | |\n| - `camera-controls disable-zoom shadow-intensity=\"0\"` | |\n| - CSS: `transform: translate(-50%, -50%) rotate(25deg)`, size `80vw × 80vh` | |\n| - `--progress-bar-color: transparent; --poster-color: transparent;` | |\n| ## Texture Swapping | |\n| On model load, preload both textures using `modelViewer.createTexture()`. Do a one-frame shader warm-up (apply blue then green) so first real swap is instant. | |\n| ``` ts | |\n| const blueTexture = await modelViewer.createTexture(ASSETS.BLUE_BASE_COLOR) | |\n| const greenTexture = await modelViewer.createTexture(ASSETS.GREEN_BASE_COLOR) | |\n| ``` | |\n| On flavor switch, apply via: | |\n| ``` ts | |\n| material.pbrMetallicRoughness.baseColorTexture.setTexture(texture) | |\n| ``` | |\n| ## Header | |\n| Glassmorphism nav bar with: | |\n| - Logo: SVG circle + cross icon with \"Soda\" text in Galada font | |\n| - Nav pills: Home (active), Ingredients, Taste, Eco, Reviews | |\n| - Active/hover state: `bg-[#fbcfe8] text-[#011d17]` | |\n| - Contact Us button: `bg-black/50` rounded pill | |\n| - Glass effect: `backdrop-blur-[20px] bg-white/8 border border-white/20` | |\n| ## Left Column | |\n| - Main title: \"Pure\" (outline) + \"Zero\" in Galada, `clamp(5rem, 10vw, 12rem)`, `line-height: 0.8` | |\n| - Description: \"Unleash the crisp taste of zero sugar...\" in Inter, muted color | |\n| - CTA: \"Shop Now\" pill button with pink `+` circle icon | |\n| - Award badge: glass icon + \"DESIGN AWARDS\" / \"PREMIUM BEVERAGE 2025\" | |\n| - Award badge pushed to bottom with `margin-top: auto` | |\n| ## Right Column | |\n| - Two flavor cards: \"Diet Classic\" ($2.99) and \"Zero Lime\" ($2.99) | |\n| - Cards: glass bg, `border-radius: 28px`, hover lifts image `-30px rotate(-12deg) scale(1.15)` | |\n| - Active card: `border-color: #fbcfe8` | |\n| - Zero Lime card image: `filter: brightness(0.7)` | |\n| - Carousel nav arrows: glass circles with ← → | |\n| - Side title: \"Refreshingly\" (outline) + \"Clean\" in Galada, right-aligned | |\n| ## Berry Positions | |\n| Foreground (z-index above text and can): | |\n| - b1: `220px` at `top: 25%; left: 30%` | |\n| - b2: `100px` at `top: 60%; left: 42%` | |\n| - b3: `250px` at `top: 30%; left: 62%` | |\n| - b4: `140px` at `top: 15%; left: 48%` | |\n| - b5: `120px` at `top: 75%; left: 20%` | |\n| - b6: `180px` at `top: 45%; left: 75%` | |\n| Background (behind everything): | |\n| - b7: `80px` at `top: 15%; left: 40%` opacity `0.7` | |\n| - b8: `70px` at `top: 50%; left: 55%` opacity `0.6` | |\n| - b9: `75px` at `top: 80%; left: 35%` opacity `0.7` | |\n| ## Leaf Positions | |\n| - l1: `60px` at `top: 10%; left: 15%` | |\n| - l2: `140px` at `top: 40%; left: 80%` opacity `0.4` | |\n| - l3: `80px` at `top: 70%; left: 75%` | |\n| - l4: `120px` at `top: 85%; left: 20%` opacity `0.3` | |\n| ## Responsive (max-width: 1200px) | |\n| - Product viewer: `width: 100vw; height: 60vh; top: 40%` | |\n| - Hero content: single column, `padding-top: 8rem` | |\n| - Titles: `font-size: 5rem` | |\n| - Right column: center-aligned | |\n| ## Assets | |\n| All assets are hosted on a CDN. Create a constants file: | |\n| ``` ts | |\n| // lib/assets.ts | |\n| export const ASSETS = { | |\n| LEAVES: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/leaves.glb', | |\n| CHERRY: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/cherry.glb', | |\n| BLUEBERRY: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/blueberry.glb', | |\n| SODA_CAN: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/deit_soda2.glb', | |\n| GREEN_SODA_IMG: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/Green%20Soda.png', | |\n| BLUE_SODA_IMG: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/Blue%20Soda.png', | |\n| GREEN_BASE_COLOR: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/green%20base%20color.jpg', | |\n| BLUE_BASE_COLOR: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/blue%20base%20color.jpg', | |\n| BUBBLE: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/bubble.png', | |\n| } as const | |\n| ``` | |\n| ## Animation Loop (requestAnimationFrame) | |\n| Run a single `requestAnimationFrame` loop that handles: | |\n| 1. Mouse interpolation (lerp 0.05) | |\n| 2. Can camera orbit update (mouse tilt + switch spin) | |\n| 3. Parallax container transforms | |\n| 4. Berry repulsion + floating (only when not switching flavors) | |\n| 5. Leaf floating | |\n| Use `useEffect` with cleanup in the main page component or a custom `useAnimationLoop` hook. | |\n| ## Important Notes | |\n| - All `model-viewer` components must be in `'use client'` components | |\n| - URL-encode spaces in asset filenames (`%20`) | |\n| - The can entrance: `.hero-center` starts `opacity: 0`, fades in over `1.5s` with `0.3s` delay, then floats ±20px infinitely | |\n| - Hide model-viewer defaults: `--progress-bar-color: transparent; --poster-color: transparent` | |\n| - Preload berry models in a hidden div to avoid lag during flavor switch | |\n| - No separate loading screen needed |", "url": "https://wpnews.pro/news/prompt-i-used-to-build-a-3d-soda-landing-page-with-grok-4-5-in-one-shot", "canonical_source": "https://gist.github.com/StarKnightt/215b05092c898ead0530a61c2b685e62", "published_at": "2026-07-11 16:20:02+00:00", "updated_at": "2026-07-12 13:39:15.255154+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "generative-ai"], "entities": ["Grok 4.5", "Next.js", "React", "TypeScript", "Tailwind CSS", "GSAP", "Google", "getlayers.ai"], "alternates": {"html": "https://wpnews.pro/news/prompt-i-used-to-build-a-3d-soda-landing-page-with-grok-4-5-in-one-shot", "markdown": "https://wpnews.pro/news/prompt-i-used-to-build-a-3d-soda-landing-page-with-grok-4-5-in-one-shot.md", "text": "https://wpnews.pro/news/prompt-i-used-to-build-a-3d-soda-landing-page-with-grok-4-5-in-one-shot.txt", "jsonld": "https://wpnews.pro/news/prompt-i-used-to-build-a-3d-soda-landing-page-with-grok-4-5-in-one-shot.jsonld"}}