import { useEffect, useRef, useState } from 'react' import { synth } from '../audio/synth' import { Btn } from './Crt' const LOAD_SECONDS = 4.2 /** * Loading from cassette, the way the listing in the magazine arrived. It is * also where the audio gets unlocked: browsers want a gesture before they * will make a sound, and pressing PLAY is exactly that gesture. */ export function BootScreen({ onLoaded }: { onLoaded: () => void }) { const [stage, setStage] = useState<'ready' | 'loading' | 'done'>('ready') const [progress, setProgress] = useState(0) const stopTape = useRef<(() => void) | null>(null) useEffect(() => () => stopTape.current?.(), []) useEffect(() => { if (stage !== 'loading') return const startedAt = performance.now() let raf = 0 const frame = (now: number) => { const p = Math.min(1, (now - startedAt) / (LOAD_SECONDS * 1000)) setProgress(p) if (p < 1) raf = requestAnimationFrame(frame) else { stopTape.current?.() setStage('done') synth.beep() window.setTimeout(onLoaded, 650) } } raf = requestAnimationFrame(frame) return () => cancelAnimationFrame(raf) }, [stage, onLoaded]) const play = () => { synth.ensure() synth.beep() stopTape.current = synth.tape(LOAD_SECONDS) setStage('loading') } const skip = () => { stopTape.current?.() onLoaded() } return (
{/* Loader stripes bleed off both edges, the way tape loaders did. */} {stage === 'loading' &&
}
ATARI 8K BASIC  REV. B
 
READY
CLOAD
{stage === 'ready' && ( <>
 
PRESS PLAY ON RECORDER, THEN RETURN.
)} {stage !== 'ready' && ( <>
 
LOADING "LEMONADE" {stage === 'loading' ? '.'.repeat(1 + Math.floor(progress * 12)) : ''}
{stage === 'done' && ( <>
 
READY
)} )}
{stage === 'ready' ? ( ► PRESS PLAY ) : ( stage === 'loading' && ( SKIP THE LOAD ) )}
) }