import React, { useState, useEffect, useRef, useMemo } from 'react'; import { Play, Pause, Disc, Wind, Target, Layers } from 'lucide-react'; // --- BIOLOGICAL AUDIO ENGINE --- class LayaEngine { constructor() { this.ctx = new (window.AudioContext || window.webkitAudioContext)(); this.masterBus = this.ctx.createGain(); this.masterBus.connect(this.ctx.destination); this.setupDrone(); } setupDrone() { // A constant 432Hz "Om" drone to ground the user this.droneOsc = this.ctx.createOscillator(); this.droneGain = this.ctx.createGain(); this.droneOsc.frequency.value = 136.1; // Frequency of Om (Sadhaka) this.droneGain.gain.value = 0.03; this.droneOsc.connect(this.droneGain); this.droneGain.connect(this.masterBus); this.droneOsc.start(); } trigger(time, type) { const osc = this.ctx.createOscillator(); const g = this.ctx.createGain(); if (type === 'sam') { // Deep resonant impact osc.frequency.setValueAtTime(60, time); osc.frequency.exponentialRampToValueAtTime(40, time + 0.4); g.gain.setValueAtTime(0.5, time); g.gain.exponentialRampToValueAtTime(0.001, time + 0.6); } else if (type === 'beat') { // Sharp clear strike osc.frequency.setValueAtTime(440, time); g.gain.setValueAtTime(0.2, time); g.gain.exponentialRampToValueAtTime(0.001, time + 0.1); } else { // Subtle subdivision "tick" osc.frequency.setValueAtTime(1200, time); g.gain.setValueAtTime(0.04, time); g.gain.exponentialRampToValueAtTime(0.001, time + 0.02); } osc.connect(g); g.connect(this.masterBus); osc.start(time); osc.stop(time + 0.6); } } const TAALS = [ { name: 'TEENTAAL', beats: 16, accent: [0, 4, 8, 12] }, { name: 'JHAPTAAL', beats: 10, accent: [0, 2, 5, 7] }, { name: 'EKTAAL', beats: 12, accent: [0, 2, 4, 6, 8, 10] }, { name: 'RUPAK', beats: 7, accent: [0, 3, 5] }, ]; const App = () => { const [mode, setMode] = useState('singularity'); // singularity | prana | mandala const [isPlaying, setIsPlaying] = useState(false); const [tempo, setTempo] = useState(80); const [taal, setTaal] = useState(TAALS[0]); const [divs, setDivs] = useState(4); const [phase, setPhase] = useState(0); // 0 to 1 cycle progress const [impact, setImpact] = useState(0); // For flash effects const audio = useRef(null); const scheduler = useRef({ nextTime: 0, matra: 0, sub: 0 }); const rafRef = useRef(); const tick = () => { const lookahead = 0.1; if (!audio.current) return; while (scheduler.current.nextTime < audio.current.ctx.currentTime + lookahead) { const { matra, sub, nextTime } = scheduler.current; const isSam = matra === 0 && sub === 0; const isBeat = sub === 0; audio.current.trigger(nextTime, isSam ? 'sam' : (isBeat ? 'beat' : 'sub')); const stepSec = (60 / tempo) / divs; scheduler.current.nextTime += stepSec; scheduler.current.sub = (sub + 1) % divs; if (scheduler.current.sub === 0) { scheduler.current.matra = (matra + 1) % taal.beats; } } rafRef.current = setTimeout(tick, 25); }; useEffect(() => { let anim; const update = () => { if (audio.current && isPlaying) { const beatSec = 60 / tempo; const totalSec = beatSec * taal.beats; // Accurate phase calculation based on audio clock const currentMatraTotal = (scheduler.current.matra * divs + scheduler.current.sub); const elapsedSinceLastScheduled = audio.current.ctx.currentTime - (scheduler.current.nextTime - (60/tempo/divs)); const p = ((currentMatraTotal / (taal.beats * divs)) + (elapsedSinceLastScheduled / totalSec)) % 1; setPhase(p); // Impact decay for visuals if (elapsedSinceLastScheduled < 0.05) setImpact(1); else setImpact(prev => prev * 0.9); } anim = requestAnimationFrame(update); }; update(); return () => cancelAnimationFrame(anim); }, [isPlaying, tempo, taal, divs]); const toggle = () => { if (!isPlaying) { if (!audio.current) audio.current = new LayaEngine(); audio.current.ctx.resume(); scheduler.current = { nextTime: audio.current.ctx.currentTime + 0.1, matra: 0, sub: 0 }; tick(); setIsPlaying(true); } else { clearTimeout(rafRef.current); setIsPlaying(false); setPhase(0); setImpact(0); } }; return (
{/* MODAL BACKGROUNDS */}
{mode === 'singularity' && } {mode === 'prana' && } {mode === 'mandala' && }
{/* UI OVERLAY */}

Becoming the Rhythm

{taal.name}

{/* PLAY BUTTON */} {/* MODE SWITCHER */}
{[ { id: 'singularity', icon: , label: 'Singularity' }, { id: 'prana', icon: , label: 'Prana' }, { id: 'mandala', icon: , label: 'Mandala' } ].map(m => ( ))}
{/* REFINED CONTROLS */}
{tempo} setTempo(parseInt(e.target.value))} className="w-2/3 accent-[#c9a962]" />
{[1, 3, 4, 8].map(d => ( ))}
{/* FLASH LAYER */}
); }; // --- VARIATION 1: THE SINGULARITY --- // Focus: Gravitational weight of the cycle. // Visual: A warped ring where space "bunches up" as you approach the Sam. const SingularityField = ({ phase, beats, impact }) => { const canvasRef = useRef(); useEffect(() => { const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); let raf; const draw = () => { const w = canvas.width = window.innerWidth; const h = canvas.height = window.innerHeight; ctx.clearRect(0,0,w,h); const cx = w/2; const cy = h/2; const radius = Math.min(w,h) * 0.3; // Draw warped path ctx.beginPath(); ctx.strokeStyle = `rgba(201, 169, 98, ${0.1 + impact * 0.4})`; ctx.lineWidth = 1; for(let i=0; i<360; i++) { const rad = i * Math.PI / 180; // Gravity effect: space bends toward the top (Sam) const warpedRad = rad + Math.sin(rad) * 0.1; const r = radius + (isActiveBeat(i, beats, phase) ? 10 * impact : 0); const x = cx + r * Math.cos(warpedRad - Math.PI/2); const y = cy + r * Math.sin(warpedRad - Math.PI/2); if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y); } ctx.stroke(); // Current Time Node const currentAngle = (phase * Math.PI * 2) - Math.PI/2; ctx.fillStyle = '#c9a962'; ctx.beginPath(); ctx.arc(cx + radius * Math.cos(currentAngle), cy + radius * Math.sin(currentAngle), 3 + impact * 5, 0, Math.PI*2); ctx.fill(); raf = requestAnimationFrame(draw); }; draw(); return () => cancelAnimationFrame(raf); }, [phase, impact]); const isActiveBeat = (angle, beats, phase) => { const beatAngle = Math.floor(phase * beats) * (360/beats); return Math.abs(angle - beatAngle) < 5; }; return ; }; // --- VARIATION 2: THE PRANA --- // Focus: Rhythm as breathing. Expansion and contraction. const PranaField = ({ phase, beats, impact }) => { const canvasRef = useRef(); useEffect(() => { const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); let raf; const draw = () => { const w = canvas.width = window.innerWidth; const h = canvas.height = window.innerHeight; ctx.clearRect(0,0,w,h); const cx = w/2; const cy = h/2; // The Breath: Cycle expands and contracts const breathScale = 1 + Math.sin(phase * Math.PI * 2 - Math.PI/2) * 0.2; const radius = Math.min(w,h) * 0.25 * breathScale; ctx.strokeStyle = '#e8e4df'; ctx.globalAlpha = 0.1 + impact * 0.5; ctx.beginPath(); ctx.arc(cx, cy, radius, 0, Math.PI * 2); ctx.stroke(); // Subdivision "Cells" for(let i=0; i cancelAnimationFrame(raf); }, [phase, impact]); return ; }; // --- VARIATION 3: THE MANDALA --- // Focus: Complexity and interference. Feeling the sub-pulse texture. const MandalaField = ({ phase, beats, divs, impact }) => { const canvasRef = useRef(); useEffect(() => { const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); let raf; const draw = () => { const w = canvas.width = window.innerWidth; const h = canvas.height = window.innerHeight; ctx.clearRect(0,0,w,h); const cx = w/2; const cy = h/2; const radius = Math.min(w,h) * 0.3; ctx.lineWidth = 0.5; ctx.strokeStyle = `rgba(201, 169, 98, ${0.2 + impact * 0.8})`; // Interference pattern based on divisions for(let i=0; i < divs * beats; i++) { const angle = (i / (divs * beats)) * Math.PI * 2 - Math.PI/2; const isActive = Math.floor(phase * divs * beats) === i; ctx.beginPath(); const startR = radius * 0.8; const endR = radius * (isActive ? 1.1 : 1); ctx.moveTo(cx + startR * Math.cos(angle), cy + startR * Math.sin(angle)); ctx.lineTo(cx + endR * Math.cos(angle), cy + endR * Math.sin(angle)); ctx.globalAlpha = isActive ? 1 : 0.1; ctx.stroke(); } raf = requestAnimationFrame(draw); }; draw(); return () => cancelAnimationFrame(raf); }, [phase, impact, divs, beats]); return ; }; export default App;