import ColorThief from 'colorthief'; const colorThief = new ColorThief(); const getImageData = (): Promise => { return new Promise( resolve => { const img = document.getElementById( 'current-image' ) as HTMLImageElement; if ( img.complete ) { resolve( colorThief.getPalette( img ) ); } else { img.addEventListener( 'load', () => { resolve( colorThief.getPalette( img ) ); } ); } } ); }; const createBackground = () => { return new Promise( resolve => { getImageData().then( palette => { const colourDetails: number[][] = []; const colours: string[] = []; let differentEnough = true; if ( palette[ 0 ] ) { for ( const i in palette ) { for ( const colour in colourDetails ) { const colourDiff = ( Math.abs( colourDetails[ colour ][ 0 ] - palette[ i ][ 0 ] ) / 255 + Math.abs( colourDetails[ colour ][ 1 ] - palette[ i ][ 1 ] ) / 255 + Math.abs( colourDetails[ colour ][ 2 ] - palette[ i ][ 2 ] ) / 255 ) / 3 * 100; if ( colourDiff > 15 ) { differentEnough = true; } } if ( differentEnough ) { colourDetails.push( palette[ i ] ); colours.push( 'rgb(' + palette[ i ][ 0 ] + ',' + palette[ i ][ 1 ] + ',' + palette[ i ][ 2 ] + ')' ); } differentEnough = false; } } let outColours = 'conic-gradient('; if ( colours.length < 3 ) { for ( let i = 0; i < 3; i++ ) { if ( colours[ i ] ) { outColours += colours[ i ] + ','; } else { if ( i === 0 ) { outColours += 'blue,'; } else if ( i === 1 ) { outColours += 'green,'; } else if ( i === 2 ) { outColours += 'red,'; } } } } else if ( colours.length < 11 ) { for ( const i in colours ) { outColours += colours[ i ] + ','; } } else { for ( let i = 0; i < 10; i++ ) { outColours += colours[ i ] + ','; } } outColours += colours[ 0 ] ?? 'blue' + ')'; resolve( outColours ); } ); } ); }; let callbackFun = () => {}; const subscribeToBeatUpdate = ( cb: () => void ) => { callbackFun = cb; micAudioHandler(); }; const unsubscribeFromBeatUpdate = () => { callbackFun = () => {}; try { clearInterval( micAnalyzer ); } catch ( e ) { /* empty */ } }; const coolDown = () => { beatDetected = false; }; let micAnalyzer = 0; let beatDetected = false; const micAudioHandler = () => { const audioContext = new ( window.AudioContext || window.webkitAudioContext )(); const analyser = audioContext.createAnalyser(); analyser.fftSize = 256; const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array( bufferLength ); beatDetected = false; navigator.mediaDevices.getUserMedia( { 'audio': true } ).then( stream => { const mic = audioContext.createMediaStreamSource( stream ); mic.connect( analyser ); analyser.getByteFrequencyData( dataArray ); let prevSpectrum: number[] = []; const threshold = 10; // Adjust as needed micAnalyzer = setInterval( () => { analyser.getByteFrequencyData( dataArray ); // Convert the frequency data to a numeric array const currentSpectrum = Array.from( dataArray ); if ( prevSpectrum ) { // Calculate the spectral flux const flux = calculateSpectralFlux( prevSpectrum, currentSpectrum ); if ( flux > threshold && !beatDetected ) { // Beat detected beatDetected = true; callbackFun(); } } prevSpectrum = currentSpectrum; }, 60 / 180 * 250 ); } ); }; const calculateSpectralFlux = ( prevSpectrum: number[], currentSpectrum: number[] ) => { let flux = 0; for ( let i = 0; i < prevSpectrum.length; i++ ) { const diff = currentSpectrum[ i ] - prevSpectrum[ i ]; flux += Math.max( 0, diff ); } return flux; }; export default { createBackground, subscribeToBeatUpdate, unsubscribeFromBeatUpdate, coolDown, };