import React, { useCallback, useState } from "react"; import { Renderer, RendererError } from "./Renderer"; import { ShaderError } from "./Shader"; function Visualizer({ audioContext, audioNode, }: { audioContext: AudioContext; audioNode: AudioNode; }) { const [renderError, setRenderError] = useState<JSX.Element | null>(null); const visualizer = useCallback( (visualizer: HTMLDivElement | null) => { // If we're rendering an error message, we won't be // setting up the visualizer. // // Also, nonintuitively, renderError will be null here on // subsequent iterations, so we can't rely on it to // identify errors. if (visualizer === null) { return; } const canvas = visualizer.children[0]; const overlay = visualizer.children[1]; if ( !(canvas instanceof HTMLCanvasElement) || !(overlay instanceof HTMLSpanElement) ) { throw new Error( "react did not create our visualizer div correctly" ); } const renderer = new Renderer( audioContext, audioNode, canvas, overlay ); try { renderer.initializeScene(); } catch (error) { // Log so we don't lose the stack trace console.log(error); if (error instanceof ShaderError) { setRenderError( <span> Failed to compile shader; This is a bug, feel free to contact me with this error message: <pre> <code className="has-text-danger"> {error.message} </code> </pre> </span> ); } else if (error instanceof RendererError) { setRenderError( <span> This browser does not support WebGL 2, sadly. This demo uses WebGL and specifically instanced drawing, so unfortunately this means it can't run on your browser/device. </span> ); } else if (error instanceof Error) { setRenderError( <span> Something went very wrong; apologies, either your browser is not behaving or there's a serious bug. You can contact me with this error message: <pre> <code className="has-text-danger"> {error.message} </code> </pre> </span> ); } else { setRenderError( <span> Something went very wrong; apologies, either your browser is not behaving or there's a serious bug. </span> ); } } }, [audioContext, audioNode] ); if (renderError === null) { return ( <div ref={visualizer} className="is-flex-grow-1 is-clipped is-relative" > <canvas style={{ display: "block" }}></canvas> <span className="is-bottom-left" style={{ display: "relative" }} ></span> </div> ); } else { return renderError; } } export default Visualizer;