cd /news/developer-tools/prompt-i-used-to-build-a-3d-soda-lan… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-56210] src=gist.github.com β†— pub= topic=developer-tools verified=true sentiment=↑ positive

Prompt I used to build a 3D soda landing page with Grok 4.5 in one shot

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.

read9 min views1 publishedJul 11, 2026

| # Recreate this interactive 3D soda landing page in Next.js + React + TypeScript + Tailwind | | | # Design and 3D assets from getlayers.ai (Soda template). This prompt recreates the design using Grok 4.5. | | | 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. | | | 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. | | | ## Project Setup | | | bash | | | pnpm create next-app@latest soda-landing --typescript --tailwind --app --eslint | | | cd soda-landing | | | pnpm add gsap | | | pnpm add @google/model-viewer | | | | | | ## What it is | | | 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. | | | 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. | | | ## Fonts | | | Add to layout.tsx via next/font/google: | | | - Body: Inter (weights 400, 500) | | | - Headings: Galada (cursive, weight 400) | | | - Nav: Manrope (weights 400, 700) | | | - Logo/Headings alt: Outfit (weights 400, 700, 900) | | | ## Color Palette | | | - Near-black: #0a0a0a | | | - Classic teal: inner #0b8a78, mid #044e3b, outer #011411 | | | - Blue theme: inner #0b4f8a, mid #04294e, outer #010c14 | | | - Pink accent: #fbcfe8 | | | - Accent text on pink: #011d17 | | | - Glass bg: rgba(255, 255, 255, 0.05) | | | - Glass border: rgba(255, 255, 255, 0.1) | | | - Muted text: rgba(255, 255, 255, 0.7) | | | ## Component Structure | | | | | | app/ | | | layout.tsx β€” fonts, metadata, global styles | | | page.tsx β€” main page component | | | globals.css β€” custom CSS (gradients, animations, model-viewer styles) | | | components/ | | | Header.tsx β€” glassmorphism nav bar | | | HeroLeft.tsx β€” "Pure Zero" title, description, CTA, award badge | | | HeroRight.tsx β€” flavor carousel cards, "Refreshingly Clean" title | | | ProductViewer.tsx β€” center 3D can with model-viewer | | | FloatingBerries.tsx β€” foreground + background berry model-viewers | | | FloatingLeaves.tsx β€” background leaf model-viewers | | | BubblesContainer.tsxβ€” rising bubble animation | | | FlavorCard.tsx β€” individual flavor card | | | | | | ## Page Layout | | | The page does NOT scroll. overflow: hidden; height: 100vh; on body. | | | The body background is a radial gradient that transitions between teal (Classic) and blue (Zero Lime) themes using GSAP CSS variable animation. | | | 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. | | | ## Key Interactions | | | ### 1. Mouse cursor tilt on 3D can | | | Per animation frame, smoothly interpolate mouse position and update model-viewer camera orbit: | | | | | | cameraOrbit = `${(currentMouse.x * 40) + switchSpin}deg ${90 + (currentMouse.y * 20)}deg 380%` | | | | | | Mouse smoothing lerp factor: 0.05 | | | ### 2. Parallax on floating elements | | | - Foreground berries: translate(mouse.x * 60px, mouse.y * 60px) | | | - Background berries: translate(mouse.x * -30px, mouse.y * -30px) | | | - Leaves: translate(mouse.x * -15px, mouse.y * -15px) | | | ### 3. Berry pointer repulsion | | | 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]. | | | ### 4. Flavor switch choreography | | | On card click: | | | 1. Background morph: GSAP animates CSS variables --bg-inner, --bg-mid, --bg-outer over 1.5s ease power2.inOut | | | 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) | | | 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) | | | ### 5. Rising bubbles | | | Spawn bubble <img> every 400ms. Size: 10-30px, opacity: 0.2-0.6, rise duration: 4-10s, drift +30px X, 360deg rotation over -110vh. | | | ### 6. Floating leaves | | | Amplitude: 20px Y, 15px X, 15deg rotation. Durations: 10 + i*2 seconds. | | | ## model-viewer Setup | | | Since @google/model-viewer is a web component, in Next.js you need to handle it as a client component. Create a wrapper: | | | tsx | | | 'use client' | | | import '@google/model-viewer' | | | // Declare the custom element for TypeScript | | | declare global { | | | namespace JSX { | | | interface IntrinsicElements { | | | 'model-viewer': any | | | } | | | } | | | } | | | | | | Center can settings: | | | - camera-orbit="0deg 90deg 380%" | | | - field-of-view="30deg" | | | - exposure="1.5" | | | - environment-image="neutral" | | | - camera-controls disable-zoom shadow-intensity="0" | | | - CSS: transform: translate(-50%, -50%) rotate(25deg), size 80vw Γ— 80vh | | | - --progress-bar-color: transparent; --poster-color: transparent; | | | ## Texture Swapping | | | 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. | | | ts | | | const blueTexture = await modelViewer.createTexture(ASSETS.BLUE_BASE_COLOR) | | | const greenTexture = await modelViewer.createTexture(ASSETS.GREEN_BASE_COLOR) | | | | | | On flavor switch, apply via: | | | ts | | | material.pbrMetallicRoughness.baseColorTexture.setTexture(texture) | | | | | | ## Header | | | Glassmorphism nav bar with: | | | - Logo: SVG circle + cross icon with "Soda" text in Galada font | | | - Nav pills: Home (active), Ingredients, Taste, Eco, Reviews | | | - Active/hover state: bg-[#fbcfe8] text-[#011d17] | | | - Contact Us button: bg-black/50 rounded pill | | | - Glass effect: backdrop-blur-[20px] bg-white/8 border border-white/20 | | | ## Left Column | | | - Main title: "Pure" (outline) + "Zero" in Galada, clamp(5rem, 10vw, 12rem), line-height: 0.8 | | | - Description: "Unleash the crisp taste of zero sugar..." in Inter, muted color | | | - CTA: "Shop Now" pill button with pink + circle icon | | | - Award badge: glass icon + "DESIGN AWARDS" / "PREMIUM BEVERAGE 2025" | | | - Award badge pushed to bottom with margin-top: auto | | | ## Right Column | | | - Two flavor cards: "Diet Classic" ($2.99) and "Zero Lime" ($2.99) | | | - Cards: glass bg, border-radius: 28px, hover lifts image -30px rotate(-12deg) scale(1.15) | | | - Active card: border-color: #fbcfe8 | | | - Zero Lime card image: filter: brightness(0.7) | | | - Carousel nav arrows: glass circles with ← β†’ | | | - Side title: "Refreshingly" (outline) + "Clean" in Galada, right-aligned | | | ## Berry Positions | | | Foreground (z-index above text and can): | | | - b1: 220px at top: 25%; left: 30% | | | - b2: 100px at top: 60%; left: 42% | | | - b3: 250px at top: 30%; left: 62% | | | - b4: 140px at top: 15%; left: 48% | | | - b5: 120px at top: 75%; left: 20% | | | - b6: 180px at top: 45%; left: 75% | | | Background (behind everything): | | | - b7: 80px at top: 15%; left: 40% opacity 0.7 | | | - b8: 70px at top: 50%; left: 55% opacity 0.6 | | | - b9: 75px at top: 80%; left: 35% opacity 0.7 | | | ## Leaf Positions | | | - l1: 60px at top: 10%; left: 15% | | | - l2: 140px at top: 40%; left: 80% opacity 0.4 | | | - l3: 80px at top: 70%; left: 75% | | | - l4: 120px at top: 85%; left: 20% opacity 0.3 | | | ## Responsive (max-width: 1200px) | | | - Product viewer: width: 100vw; height: 60vh; top: 40% | | | - Hero content: single column, padding-top: 8rem | | | - Titles: font-size: 5rem | | | - Right column: center-aligned | | | ## Assets | | | All assets are hosted on a CDN. Create a constants file: | | | ts | | | // lib/assets.ts | | | export const ASSETS = { | | | LEAVES: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/leaves.glb', | | | CHERRY: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/cherry.glb', | | | BLUEBERRY: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/blueberry.glb', | | | SODA_CAN: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/deit_soda2.glb', | | | GREEN_SODA_IMG: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/Green%20Soda.png', | | | BLUE_SODA_IMG: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/Blue%20Soda.png', | | | GREEN_BASE_COLOR: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/green%20base%20color.jpg', | | | BLUE_BASE_COLOR: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/blue%20base%20color.jpg', | | | BUBBLE: 'https://api.getlayers.ai/storage/v1/object/public/public/assets/soda-14ff8a788d/bubble.png', | | | } as const | | | | | | ## Animation Loop (requestAnimationFrame) | | | Run a single requestAnimationFrame loop that handles: | | | 1. Mouse interpolation (lerp 0.05) | | | 2. Can camera orbit update (mouse tilt + switch spin) | | | 3. Parallax container transforms | | | 4. Berry repulsion + floating (only when not switching flavors) | | | 5. Leaf floating | | | Use useEffect with cleanup in the main page component or a custom useAnimationLoop hook. | | | ## Important Notes | | | - All model-viewer components must be in 'use client' components | | | - URL-encode spaces in asset filenames (%20) | | | - The can entrance: .hero-center starts opacity: 0, fades in over 1.5s with 0.3s delay, then floats Β±20px infinitely | | | - Hide model-viewer defaults: --progress-bar-color: transparent; --poster-color: transparent | | | - Preload berry models in a hidden div to avoid lag during flavor switch | | | - No separate screen needed |

── more in #developer-tools 4 stories Β· sorted by recency
── more on @grok 4.5 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/prompt-i-used-to-bui…] indexed:0 read:9min 2026-07-11 Β· β€”