As developers, we are accustomed to showcasing our work through traditional, chronological grid-based portfolios. While functional, standard grids can feel static and fail to capture the visceral excitement of Shipping.
To mark the milestone of Build #200, we wanted to create something fundamentally different: a fully gamified, tactile, and sensory-rich exploration engine.
Enter DailyBuild Roulette, a slot-machine-style discovery interface built using React, TypeScript, Framer Motion (motion/react), and Tailwind CSS v4.
In this article, we'll dive deep into the technical implementation of its three standout features:
- The 3-Phase Mechanical Deceleration Spindle
- Dynamic Web Audio Sound Synthesis
- Cross-Platform Device-Shake Gesture Hooks
- Tailwind CSS v4 Stark Aesthetics (Ditching the "AI Gradient Slop")
1. Stark Minimalism: Why We Ditched the Gradients
In contemporary frontend design, there is a tendency to overuse vibrant radial gradients, blinking neon margins, and floating glassmorphism overlays. We call this "AI Slop." It distracts from content and results in visual clutter.
For DailyBuild Roulette, we chose a high-contrast flat layout.
- We replaced gradient background rings and radial vignettes with flat solid boundaries (
#0a0a0abackground and#0d0d0dpanels). - Shadows are tight and specific, creating elevation instead of blurry dark clouds.
- Accents use solid colors (such as deep Indigo and Amber) paired with high-contrast text overlays to enforce strict hierarchy.
The typography relies on Inter paired with JetBrains Mono for numbers and indices, creating a highly technical, functional, and editorial feel.
2. The Mechanics of the Spin: 3-Phase Deceleration Spindle
The heart of the application is the SlotMachine.tsx component. A generic slide-in transition would ruin the "physical weight" illusion of a casino spindle. To solve this, we used Framer Motion's imperative useAnimationControls to chain together three specific phases of motion.
The Physics Sequence
-
Phase 1: Rapid Acceleration: The strip quickly rolls down from index
0to cover about 35% of the card height distance. - Phase 2: Viscous Friction Deceleration: The wheel slows down heavily using an ease-out curve, traveling past the target element to simulate physical inertia (overshoot).
- Phase 3: Elastic Snap-back: The card rebounds back to lock precisely onto the center using a spring physics model.
Here is the production implementation:
const runSpinSequence = async () => {
// Ensure we start exactly at zero scroll coordinate
controls.set({ y: 0 });
// Phase 1: Rapid linear-ease acceleration
await controls.start({
y: targetY * 0.35,
transition: {
duration: 0.7,
ease: [0.4, 0, 1, 1], // Ease In curve
},
});
// Phase 2: Heavy deceleration with physical overshoot (-12px)
await controls.start({
y: targetY - 12,
transition: {
duration: 1.6,
ease: [0, 0, 0.2, 1], // Deceleration easeOut curve
},
});
// Phase 3: Elastic spring snap back to lock exactly on targeted card
await controls.start({
y: targetY,
transition: {
type: 'spring',
stiffness: 280,
damping: 14,
mass: 0.8,
},
});
onSpinComplete();
};
This multi-phase approach creates a highly realistic, responsive, and satisfying "mechanical" sensation when landing on a card.
3. High-Fidelity Audio System Without Static Assets
Relying on pre-recorded audio files for slot machine ticking sounds introduces latency, increases bundle size, and prevents audio from scaling dynamically with the velocity of the wheel.
Instead, we built a declarative sound ticker system using the browser's native sound capabilities or high-frequency interval modulation. We simulated ticks that scale in delay as the spin progress increases:
const playTicks = () => {
if (currentCardIndex >= totalCards) return;
// Emit a click event or play sound
playClickSound();
currentCardIndex++;
// Delay calculation: scales exponentially based on rotational progress
const progress = currentCardIndex / totalCards;
const delay = 45 + Math.pow(progress, 3.5) * 450; // Delay increases up to 500ms
tickTimeoutRef.current = setTimeout(playTicks, delay);
};
This mimics the mechanical "plink" of the gear indicator slowing down, syncing the auditory feedback perfectly with the visual deceleration curve.
4. Interactive hardware: Device Shaking on Mobile
We wanted physical interaction on mobile devices to trigger spins. To do this without importing heavy sensory packages, we wrote a custom React Hook useShake.ts wrapping the browser's DeviceMotionEvent.
To prevent false triggers from basic walking or pocket movement, we calculate a multi-dimensional magnitude and evaluate it against a strict threshold:
import { useEffect } from 'react';
export function useShake(onShake: () => void, isDisabled: boolean = false) {
useEffect(() => {
if (isDisabled) return;
let lastX: number | null = null;
let lastY: number | null = null;
let lastZ: number | null = null;
let lastUpdate = 0;
const threshold = 14; // Acceleration threshold (m/s²)
const handleMotion = (event: DeviceMotionEvent) => {
const acc = event.accelerationIncludingGravity;
if (!acc) return;
const { x, y, z } = acc;
if (x === null || y === null || z === null) return;
const now = Date.now();
if (now - lastUpdate > 100) {
lastUpdate = now;
const deltaX = Math.abs(x - (lastX ?? x));
const deltaY = Math.abs(y - (lastY ?? y));
const deltaZ = Math.abs(z - (lastZ ?? z));
const magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);
if (deltaX > threshold || deltaY > threshold || deltaZ > threshold || magnitude > threshold) {
onShake();
}
lastX = x;
lastY = y;
lastZ = z;
}
};
window.addEventListener('devicemotion', handleMotion);
return () => window.removeEventListener('devicemotion', handleMotion);
}, [onShake, isDisabled]);
}
This utilizes native mobile browser APIs to create an immersive, tactile experience without external bloat.
By combining:
- Tailored CSS Layout boundaries in Tailwind v4
- Multi-phase animation choreography in Framer Motion
- Native Browser acceleration and sound interfaces
we were able to build a highly responsive, performant, and fun exploration showcase. If you're building a portfolio or gallery, consider gamifying elements of it - it elevates a collection of code into an unforgettable experience.
Project built as Build #200 by **Harish Kotra* (harishkotra.me).*
View the main portfolio at *dailybuild.xyz*.
Code & more: https://www.dailybuild.xyz/project/200-dailybuild-roulette
Try it here: https://dailybuild-roulette.vercel.app/
Top comments (0)