Ave Muji Cards
An interactive 3D collectible card experience leveraging a Physically Based Rendering (PBR) pipeline, Three.js, and physics-driven pointer tilt.
This project is an interactive 3D card experiment inspired by physical foil collectibles. The implementation breaks down into three distinct layers: texture asset generation, 3D scene setup (Three.js & React Three Fiber), and physics-driven pointer interactions.
1. The PBR Asset Pipeline
To achieve a realistic embossed gold appearance without building a heavy, high-polygon 3D mesh, the card uses a single flat plane (PlaneGeometry) combined with baked texture maps:
- Base Map (Albedo): The clean 2D artwork and base coloration without baked shadows.
- Normal Map: Generated from height and bevel data to give tactile depth to borders, typography, and card contours.
- Roughness Map: Encodes micro-surface variation. Matte areas scatter light softly, while metallic engravings reflect light sharply.
- Metalness: Configured directly on the physical material as
metalness: 1.0, instructing the shader to treat the card as conductive metallic foil.



2. Three.js / R3F Material & Environment Setup
Metallic surfaces cannot reflect convincing highlights from simple point or directional lights alone. The scene utilizes Three.js's physical shader material combined with Image-Based Lighting (IBL) via an Environment Map (such as <Environment preset="city" />).
As the card rotates, surface normals sample the HDR reflection probe, producing real-time glints across the foil engravings:
// Texture loader pipeline
const [albedo, normal, roughness] = useTexture([
'/ticket/albedo.png',
'/ticket/normal.png',
'/ticket/roughness.png'
]);
// Material definition
<mesh geometry={ticketGeometry}>
<meshPhysicalMaterial
map={albedo}
normalMap={normal}
normalScale={[0.6, 0.6]}
roughnessMap={roughness}
metalness={1.0}
roughness={0.8}
clearcoat={0.3}
clearcoatRoughness={0.1}
envMapIntensity={1.5}
/>
</mesh>3. Pointer-Based 3D Physics and Tilt
The interaction is driven by tracking pointer coordinates relative to the card's bounding box and feeding those values into damped springs or lerped rotations:
- Normalized Pointer Coordinates: Calculates mouse or touch offset from the center of the card from
-1.0to1.0. - Rotation Clamping: Rotations are clamped (
rotation.y = mouseX * maxAngleandrotation.x = -mouseY * maxAngle) to keep perspective natural. - Dynamic Light Tracking: A local point light is simultaneously offset to mirror the cursor position, emphasizing normal-mapped highlights directly under the user's cursor.
- Spring Damping: Adds smooth inertia so the card drifts back to its resting position when the cursor leaves the frame.
- Mobile Gyroscope: On phones supporting
DeviceOrientationEvent,gamma(tilt left/right) andbeta - 45°(pitch) map directly to rotation angles, with sensor jitter smoothed via lerp damping.