Svg loading is slow

Hello guys, anyone have an idea of a more efficient way to load this balloon into the 3d world. In the code below you can see how I am loading it. The texture created is being applied to a sprite. During the initial load, my screen goes black and messes with my fucntionality. When i disable this svg loading, it works correctly. I cannot load them as pngs because the state of the ballon is determined dynamically based on the current state. The balloon has 3 modes which determine color, three types which determine the central part, and if its part of a route I need to number them correctly

export const useSVGTexture = (
  svgString: string,
  width: number,
  height: number,
  isURI = false
) => {
  const [texture, setTexture] = useState<CanvasTexture | null>(null);
  const imgLoader = useLoader(TextureLoader, svgString);

  useEffect(() => {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    const img = new Image();
    img.src = isURI ? svgString : imgLoader.image.currentSrc;

    const imgLoaded = new Promise<void>((resolve) => {
      if (img.complete) {
        resolve();
      } else {
        img.onload = () => resolve();
      }
    });

    imgLoaded.then(() => {
      canvas.width = width;
      canvas.height = height;
      ctx.drawImage(img, 0, 0, width, height);

      const canvasTexture = new CanvasTexture(canvas);
      setTexture(canvasTexture);

      // Cleanup: might not be necessary to manually remove the canvas or image in a React environment,
      // as garbage collection should handle it once the components unmount.
      // However, if you create a lot of textures or have memory concerns, you might consider additional cleanup strategies.
    });
  }, [svgString, width, height, imgLoader.image.currentSrc, isURI]);

  return texture;
};
const Map3DSpriteBalloonTopComponent = (
  props: Map3DSpriteBalloonTopProps
): JSX.Element | null => {
  const { type, mode, position, dimmed, routeNumber } = props;
  let circleColor;
  let svgName;
  switch (type) {
    case Map3DBalloonSpriteType.HIVE:
      svgName = 'mapSpriteBalloonHive';
      break;

    case Map3DBalloonSpriteType.LOCATION:
      svgName = 'mapSpriteBalloonLocation';
      break;

    case Map3DBalloonSpriteType.ROUTE:
      svgName = 'mapSpriteBalloonRoute';
      break;

    default:
      svgName = 'mapSpriteBalloonRoute';
      break;
  }

  switch (mode) {
    case Map3DBalloonSpriteMode.EDIT:
      circleColor = '#21A479';
      break;

    case Map3DBalloonSpriteMode.ERROR:
      circleColor = '#FD9022';
      break;

    case Map3DBalloonSpriteMode.VIEW:
      circleColor = '#4D8FFA';
      break;

    default:
      circleColor = '#4D8FFA';
      break;
  }
  let svgString = svgs[svgName].replace('CIRCLE_COLOR', circleColor);
  if (type === Map3DBalloonSpriteType.ROUTE && routeNumber) {
    svgString = svgString.replace('CIRCLE_NUMBER', String(routeNumber));
  }

  const svgUri = toSvgDataUri(svgString);
  const texture = useSVGTexture(svgUri, 500, 500, true);

  if (!texture) return null;

  return (
    <sprite
      position={position}
      scale={[1.5, 2.15, 1.5]}
      center={new Vector2(0.5, 0.05)} // change the rotation center to be at the bottom instead of the middle of the sprite
    >
      <spriteMaterial opacity={dimmed ? 0.6 : 1} map={texture} />
    </sprite>
  );
};

i think threes svg loader is probably on the slow side. i would make the balloon in threejs directly using GitHub - pmndrs/uikit: 🎨 user interfaces for react-three-fiber now it’s part of the world and contents can dynamically change.

here’s a similar example also using uikit x.com all the UI in there is made with it.

there’s also a html to uikit converter, you can use plain html, css/tailwind to make UI shapes https://html23.com

This looks interesting, will definitely check it out for future cases. It turned out the problem in my case was that I forgot to wrap the svg loader in a Suspense