• About Us
  • Privacy Policy
  • Disclaimer
  • Contact Us
AimactGrow
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing
No Result
View All Result
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing
No Result
View All Result
AimactGrow
No Result
View All Result

From Flat to Spatial: Making a 3D Product Grid with React Three Fiber

Admin by Admin
February 24, 2026
Home Coding
Share on FacebookShare on Twitter



Product grids are the white-box gallery of e-commerce — impartial by default, inoffensive by design. Which is unusual, as a result of the bodily experiences that truly transfer product have at all times identified that setting is a part of the promote. Lighting makes choices. Association communicates worth. The area has a standpoint.

The online model normally opts out of all of that.

I needed to see what it might take to shut that hole — not as a novelty, however as a real try to make shopping really feel like being someplace. This text walks by means of how I constructed it: a curved 3D product grid utilizing React Three Fiber, a topographic GLSL background, holographic choice states, and a spring-damped digital camera rig. Alongside the way in which there are some patterns value stealing — round shader structure, animation interruptibility, and the place to attract the road between React state and mutable refs.

The Stack

The mission runs on Subsequent.js, React Three Fiber, Tailwind, and Movement. The 2 customized shaders are written in GLSL and imported as ES modules by means of a glslify webpack pipeline.

The glslify setup is value calling out as a result of it’s the one piece of infrastructure that makes shader improvement really feel trendy. A two-loader chain in subsequent.config.mjs lets us write #pragma glslify: snoise = require('glsl-noise/simplex/2nd') inside our GLSL and import the compiled consequence as a string.

Structure

The system has 4 layers, and understanding the place every one begins and stops is what retains the mission clear:

┌─────────────────────────────────────────────────┐
│  DOM Layer (Framer Movement)                      │
│  Management bar, filters, minimap, overlays        │
├─────────────────────────────────────────────────┤
│  Scene Layer (React Three Fiber)                │
│  Canvas, digital camera rig, lighting                   │
├─────────────────────────────────────────────────┤
│  Tile Layer (per-card useFrame loops)           │
│  Place, scale, opacity, shader uniforms      │
├─────────────────────────────────────────────────┤
│  Shader Layer (uncooked GLSL)                        │
│  Topography background, holographic card sheen  │
└─────────────────────────────────────────────────┘

Knowledge movement. Shoe knowledge is a JSON array. Every assortment (Nike, New Stability, Price range) maps to a separate array. Filters slender inside a group; assortment switches swap the complete array.

Interplay loop. Pointer occasions on the canvas replace a mutable rigState object. The digital camera rig reads that each body and damps towards the goal. Every tile reads the identical rigState to know whether it is chosen, then adjusts its personal place, scale, and shader uniforms.

The choice that formed every thing was what to place in React state versus mutable refs. I discovered this the laborious approach: something that adjustments at 60fps — digital camera place, tile animation progress, shader uniforms — can’t dwell in React state. The reconciliation overhead kills you. These values dwell in plain mutable objects that useFrame callbacks learn immediately. React state is reserved for discrete person actions: which assortment is energetic, which filters are set, which tile is chosen.

The Grid

The primary drawback was format. I wanted to take a flat checklist of footwear and organize them in a centered grid in 3D area, with sufficient flexibility to help filtering (which adjustments the merchandise depend) and assortment switching (which adjustments every thing).

Configuration

All grid parameters dwell in a mutable singleton — not React state, not context, only a plain object:

const CONFIG = {
  gridCols: 8,
  itemSize: 2.5,
  hole: 0.4,
  zoomIn: 12,
  zoomOut: 31,
  curvatureStrength: 0.06,
  dampFactor: 0.2,
  tiltFactor: 0.08,
  cullDistance: 14,
};

I wired each worth into Leva debug controls throughout improvement. Dragging a “curvature” slider and watching the grid bowl deepen in actual time was invaluable for dialing in really feel — one thing you can’t do with hardcoded constants and a refresh cycle.

Positioning

Tile positions come from easy column-major math, centered on the origin:

const spacing = CONFIG.itemSize + CONFIG.hole;
const col = filteredIdx % CONFIG.gridCols;
const row = Math.flooring(filteredIdx / CONFIG.gridCols);
const x = col * spacing - gridWidth / 2 + spacing / 2;
const y = -(row * spacing) + gridHeight / 2 - spacing / 2;

X runs left-to-right. Y runs top-to-bottom. Z is totally reserved for depth results — curvature, focus, and transition animations. Maintaining Z free turned out to be one of many higher early choices, as a result of it meant I might layer a number of depth results additively with out them combating one another.

The Playing cards

Every shoe is a ShoeTile — a containing a hit-test airplane, a picture mesh with our customized shader materials, textual content labels, and a detailed button.

Textures

I preload each texture at module stage earlier than any part mounts. This was non-negotiable — with out it, switching collections brought about seen pop-in as textures uploaded to the GPU one after the other:

footwear.forEach((shoe) => {
  useTexture.preload(shoe.image_url);
});

Every tile computes aspect-correct dimensions from the loaded texture so photos are by no means stretched.

The Animation Loop

That is the guts of the mission. Each tile runs its personal useFrame callback — a operate that executes each body, managing a set of animation values that compose into the ultimate rendered state.

I attempted GSAP early on and deserted it. The issue is interruptibility. If a person clicks a shoe whereas a filter transition is mid-flight, each animation must easily redirect. Timeline-based techniques battle this — you spend extra time managing cancellation than writing animation logic. CSS animations had been by no means an possibility; they can’t attain into WebGL uniforms.

I landed on easing.damp() from the great maath — a frame-rate-independent exponential damping operate. Set a goal, and the worth chases it. Change the goal mid-animation, and the worth redirects. No cleanup, no cancellation.

const focusZ = useRef(0);
const curveZ = useRef(0);
const transitionZ = useRef(0);
const animatedPos = useRef({ x, y });
const filterOpacity = useRef(1);
const filterScale = useRef(1);

The ultimate place is a composite of those unbiased channels:

ref.present.place.set(
  x,
  y + transitionY.present,
  curveZ.present + focusZ.present + transitionZ.present
);

Three Z contributions stack additively: curvature pushes distant tiles away, focus pops the chosen card ahead, transition offsets deal with enter/exit. Every damps at its personal velocity. They by no means battle as a result of they merely add.

Customized Shaders

I wrote two customized GLSL supplies utilizing drei’s shaderMaterial() helper, which provides you a declarative JSX interface () backed by uncooked GLSL.

I selected per-material shaders over post-processing for a particular purpose: my results are interaction-driven and per-card. The holographic sheen solely seems on the chosen card. A post-processing bloom cross would course of each pixel on display screen to have an effect on one card. Maintaining the impact within the materials means zero overhead for the opposite 59.

Topography Background

The background is an animated contour-line area — a dwelling topographic map that provides the scene a technical, CAD-like depth with out competing with the shoe imagery.

How the Isolines Work

The fragment shader samples 2D simplex noise (imported by way of glslify) and drifts it slowly over time:

#pragma glslify: snoise = require('glsl-noise/simplex/2nd')
float n = snoise(noiseUv * uScale + uTime * 0.05);

The contour traces come from a basic isoline extraction approach. Multiply the noise by a frequency, take the fractional half to create repeating bands, then carve skinny traces on the band boundaries with a smoothstep pair:

float traces = fract(n * 5.0);
float sample = smoothstep(0.5 - uLineThickness, 0.5, traces)
              - smoothstep(0.5, 0.5 + uLineThickness, traces);

The 2 smoothstep calls create a slender peak at 0.5 — precisely the place every band wraps round. uLineThickness (default 0.03) controls line width. The 5.0 multiplier controls what number of concentric rings seem per noise octave. I spent some time tuning these — too thick and it appears like a loading spinner, too skinny and it disappears on low-DPI screens.

Masking and Grain

A round masks feathers the sides, and movie grain prevents banding:

float grain = (fract(sin(dot(vUv * 2.0, vec2(12.9898, 78.233))) * 43758.5453) - 0.5) * 0.15;
vec3 finalColor = uColor + grain;
gl_FragColor = vec4(finalColor, sample * opacity * masks * uOpacity);

The entire thing sits on a airplane at Z -15 with depthWrite={false} and renderOrder={-1} so it by no means occludes the playing cards. When the person zooms right into a shoe, uOpacity fades to 0.25 — the background recedes with out disappearing.

Holographic Card Materials

The cardboard materials provides a holographic sheen sweep when a card is chosen. This was essentially the most enjoyable shader to write down as a result of the impact is totally pushed by a single uniform: uActive.

Vertex Respiratory

The vertex shader applies a delicate sine-wave scale oscillation on chosen playing cards:

float breath = sin(uTime * 2.0) * 0.015 * uActive;
float scale = 1.0 + breath;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos * scale, 1.0);

When uActive is 0, respiration multiplies to zero — no work for unselected playing cards.

The Sheen Sweep

The fragment shader’s sheen impact was a contented accident. I initially needed a static holographic gradient, however mapping the sheen place on to uActive created this sweep animation at no cost — because the uniform animates from 0 to 1, the band naturally slides throughout the cardboard:

float diagonal = (vUv.x * 0.8) + vUv.y;
float sheenPos = uActive * 2.5;
float sheenWidth = 0.5;
float dist = abs(diagonal - sheenPos);
float depth = 1.0 - smoothstep(0.0, sheenWidth, dist);
depth = pow(depth, 3.0);

The 0.8 multiplier on the X-axis is the “tilt” issue. In a typical $x + y$ setup, the gradient strikes at an ideal 45° angle. By weighting the X-axis barely lower than the Y, we rotate the sweep line to be extra vertical, which feels extra pure for a card being held as much as a lightweight supply.

The pow(depth, 3.0) is our “focus” management. With out it, the sheen is a large, muddy wash. By elevating the depth to an influence, we push the decrease values towards zero and maintain solely the height, sharpening the falloff from a delicate glow right into a concentrated, high-end “specular” streak.

A fade-out on the finish prevents the sheen from sticking:

float sheenFade = 1.0 - smoothstep(0.7, 1.0, uActive);
vec3 sheenColor = vec3(0.85, 0.92, 1.0) * depth * 0.9 * sheenFade;
vec3 finalColor = baseColor + sheenColor * texColor.a;

The cool blue-white shade is additive, masked by the feel’s alpha to remain throughout the shoe silhouette.

Uneven Timing

One small element that made an enormous distinction: I animate uActive with completely different damping speeds for choice and deselection:

const activeDamp = isActive ? 0.6 : 0.15;
easing.damp(imageRef.present.materials, "uActive", isActive ? 1 : 0, activeDamp, delta);

Sluggish in (0.6s), quick out (0.15s). You savor the reveal however by no means look ahead to the dismiss. This asymmetry is sufficiently subtle that customers don’t consciously discover it, however eradicating it makes the entire interplay really feel sluggish.

The Digital camera Rig

I constructed a customized digital camera rig from scratch as a substitute of utilizing drei’s OrbitControls. OrbitControls offers you a rotating digital camera orbiting a middle level — I wanted a 2D panning digital camera with bounded drag, rubber-band edges, and velocity-based tilt. Each constraint in OrbitControls would have fought me.

How It Works

The rig is a mutable singleton shared between the digital camera part and each tile:

const rigState = {
  goal: new THREE.Vector3(0, 2, 0),
  present: new THREE.Vector3(0, 2, 0),
  velocity: new THREE.Vector3(0, 0, 0),
  zoom: CONFIG.zoomOut,
  isDragging: false,
  activeId: null,
};

Pointer occasions replace goal. Each body, present damps towards goal. The digital camera reads present. This indirection is what makes every thing really feel clean — person enter is rarely utilized immediately.

Drag and Bounds

I distinguish clicks from drags utilizing a distance threshold (5px desktop, 15px contact). Drag sensitivity scales with digital camera distance so panning feels constant at any zoom stage.

Previous the grid edges, rubber-band resistance kicks in — you’ll be able to overdrag 25% earlier than a tough clamp. On launch, the digital camera snaps again. It’s the identical sample iOS makes use of for scroll bounce, and it communicates “you’ve gotten reached the sting” and not using a laborious cease.

Choice

Clicking a tile triggers a simultaneous pan and zoom. The chosen card scales to 1.5x and pops ahead 2 models on Z. All different playing cards shrink to 0.5x and fade to fifteen% opacity — a dramatic highlight.

Filtering and Assortment Switching

The app helps two sorts of transitions, and the fascinating half is that they require basically completely different methods.

In-Place Filtering

If you filter inside a group (say, “All” to “Jordan”), I don’t unmount and remount tiles. That will imply texture re-uploads, which implies body drops. As an alternative, matching objects easily reposition to fill a denser grid whereas non-matching objects fade and shrink in place:

easing.damp(animatedPos.present, "x", basePos.x, 0.2, delta);
easing.damp(animatedPos.present, "y", basePos.y, 0.2, delta);
const targetFilterOpacity = matchesFilter ? 1 : 0;
const targetFilterScale = matchesFilter ? 1 : 0.5;
easing.damp(filterOpacity, "present", targetFilterOpacity, 0.06, delta);

Hidden tiles keep mounted however invisible — seen = false as soon as opacity drops under 0.01. This implies filter adjustments are instantaneous. No GPU work, simply uniform adjustments and a place recalculation.

Assortment Switching

Switching collections is a heavier operation — totally completely different shoe knowledge. I solved this with a layer stack: the outdated grid and new grid coexist briefly, every rendering as a separate part with a singular React key.

const handleCollectionSwitch = (index) => {
  setGridLayers((prev) => {
    const exitingLayers = prev.map((layer) =>
      layer.mode === "enter"
        ? { ...layer, mode: "exit", startTime: now }
        : layer
    );
    const newLayer = {
      id: `grid-${index}-${now}`,
      objects: collectionsData[index],
      mode: "enter",
      startTime: now,
    };
    return [...exitingLayers, newLayer];
  });
  setTimeout(() => {
    setGridLayers((prev) => prev.filter((l) => l.mode === "enter"));
  }, CONFIG.cleanupTimeout);
};

The outdated grid flies towards the digital camera (Z +20) whereas the brand new one arrives from behind (Z -50). Every tile will get a random stagger delay. The impact reads as an explosion fairly than a slide — deliberate. A easy crossfade felt flat. The Z-axis motion creates a way of bodily area, and the random stagger prevents the mechanical really feel of synchronized movement.

Coming into tiles additionally unfold on Y based mostly on their grid place — prime objects begin larger, backside objects decrease — making a “convergence from all instructions” really feel.

Polish

The Dynamic Island

The underside management bar borrows Apple’s Dynamic Island sample: a single glassmorphic container that morphs between states. I used Framer Movement’s format prop for this as a result of it handles one thing CSS can’t — animating between fully completely different DOM constructions.

MiniMap

A 2D overlay runs its personal requestAnimationFrame loop, unbiased of R3F. Every shoe is a dot, the chosen shoe glows gold, and a white rectangle exhibits the seen viewport. On choice, the minimap easily zooms to 2.5x across the energetic dot.

Efficiency

Three strategies stored us at 60fps:

Time-sliced mounting. Mounting 60 textured playing cards without delay causes a GPU spike. I mount 5 per body as a substitute, spreading the work throughout ~200ms. Quick sufficient to be invisible, sluggish sufficient to stop jank. I couldn’t use InstancedMesh right here — every card has a singular texture, distinctive labels, and distinctive shader state. Instancing wants shared supplies.

Three-level culling. Each tile checks: has it absolutely exited? (skip the complete useFrame callback.) Is it past the view distance? (conceal it.) Is its opacity close to zero? (seen = false.) These checks compound — a tile that has exited a group swap skips all per-frame work, not simply rendering.

Mutable every thing. Digital camera place, tile animation refs, shader uniforms — all mutated immediately in useFrame, by no means touching React state. The one re-renders occur on discrete person actions: deciding on a tile, altering a filter, switching a group.

Conclusion

If I needed to compress the entire mission into one takeaway, it might be this: the laborious half shouldn’t be the 3D. The laborious half is making the 3D disappear. No person ought to take a look at this and suppose “oh, a WebGL demo.” They need to simply really feel like shopping footwear is barely extra fascinating than it normally is.

The patterns that bought me there — exponential damping over tweens, per-material shaders over post-processing, mutable refs over React state for something that strikes — should not significantly unique. They’re what falls out naturally while you cease treating React Three Fiber as a demo framework and begin treating it as a manufacturing one. More often than not I spent on this mission was not writing shaders. It was tuning damping constants, killing pointless re-renders, and ensuring a filter change mid-animation didn’t break one thing else.

In case you are constructing one thing related, steal the structure: React owns construction, GLSL owns pixels, and a skinny layer of mutable state bridges the 2 at 60fps. The whole lot else is style.

Tags: CreatingFiberflatgridproductReactspatial
Admin

Admin

Next Post
Resident Evil Requiem Director Admits Capcom Was Initially ‘Skeptical’ About Nintendo Change 2 Efficiency

Resident Evil Requiem Director Admits Capcom Was Initially 'Skeptical' About Nintendo Change 2 Efficiency

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recommended.

The best way to write a knowledge classification coverage, with template

The best way to write a knowledge classification coverage, with template

August 17, 2025
Examine Level Provides AI Utility Protection With Lakera Buy

Examine Level Provides AI Utility Protection With Lakera Buy

September 16, 2025

Trending.

The right way to Defeat Imagawa Tomeji

The right way to Defeat Imagawa Tomeji

September 28, 2025
Introducing Sophos Endpoint for Legacy Platforms – Sophos Information

Introducing Sophos Endpoint for Legacy Platforms – Sophos Information

August 28, 2025
AI-Assisted Menace Actor Compromises 600+ FortiGate Gadgets in 55 Nations

AI-Assisted Menace Actor Compromises 600+ FortiGate Gadgets in 55 Nations

February 23, 2026
How Voice-Enabled NSFW AI Video Turbines Are Altering Roleplay Endlessly

How Voice-Enabled NSFW AI Video Turbines Are Altering Roleplay Endlessly

June 10, 2025
Rogue Planet’ in Growth for Launch on iOS, Android, Change, and Steam in 2025 – TouchArcade

Rogue Planet’ in Growth for Launch on iOS, Android, Change, and Steam in 2025 – TouchArcade

June 19, 2025

AimactGrow

Welcome to AimactGrow, your ultimate source for all things technology! Our mission is to provide insightful, up-to-date content on the latest advancements in technology, coding, gaming, digital marketing, SEO, cybersecurity, and artificial intelligence (AI).

Categories

  • AI
  • Coding
  • Cybersecurity
  • Digital marketing
  • Gaming
  • SEO
  • Technology

Recent News

White Home Rolls Out International AI Initiatives

White Home Rolls Out International AI Initiatives

February 24, 2026
Resident Evil Requiem Director Admits Capcom Was Initially ‘Skeptical’ About Nintendo Change 2 Efficiency

Resident Evil Requiem Director Admits Capcom Was Initially ‘Skeptical’ About Nintendo Change 2 Efficiency

February 24, 2026
  • About Us
  • Privacy Policy
  • Disclaimer
  • Contact Us

© 2025 https://blog.aimactgrow.com/ - All Rights Reserved

No Result
View All Result
  • Home
  • Technology
  • AI
  • SEO
  • Coding
  • Gaming
  • Cybersecurity
  • Digital marketing

© 2025 https://blog.aimactgrow.com/ - All Rights Reserved