DEV Community

Cover image for GLSL Canvas Component for React, Hooks & Typescript
samasastudio
samasastudio

Posted on

3 1

GLSL Canvas Component for React, Hooks & Typescript

Hey Devs,

Recently I've been spending some creative time in KodeLife and designing a small library of GLSL shaders that I like to have on hand for projects. When finally rendering these shaders on the web, I can't recommend glslCanvas ๐Ÿ‘จ๐Ÿปโ€๐ŸŽจ enough.

Canvas

There are a few common canvas-related bugs that you may run into when dealing with CSS sizing, resolution and adaptive resizing though. Usually this comes in the form of pixelated shaders, poor u_mouse movement mapping, and the shader just rendering overall at the wrong size. ๐Ÿ‘พ So! I took the time experiment with the glslCanvas rendering lifecycle and React's useRef to make a handy functional component that skips you right past these bugs and directly to an easy-to-use canvas the fills whatever container you want to wrap it in. ๐Ÿฑ

Resizing helper function

To start, here's a simple helper function that will save you some headache when adaptively sizing the canvas:

  const resizer = (
    canvas: HTMLCanvasElement,
    container: HTMLDivElement
  ): void => {
    canvas.width = container.clientWidth + window.devicePixelRatio;
    canvas.height = container.clientHeight + window.devicePixelRatio;
    canvas.style.width = container.clientWidth + "px";
    canvas.style.height = container.clientHeight + "px";
  };
Enter fullscreen mode Exit fullscreen mode

GLSL Freebie

Also if you haven't worked with GLSL before and want to try out the component, here's a a solid one that works right out of the box without any images. โœจ Just pass in 'frag' as your prop for frag in the component.

Alt Text

The Component

Last but not least here is the entire component. It contains the helper function for sizing the canvas element, as well as a loop that helps set each uniform you want to pass in through the setUniform prop. ๐Ÿฅ‹ For example, if you wanted to set a uniform called u_image with the value of an image variable, then you could pass in {u_image: image}. Also pay close attention to the sequence that glslCanvas instantiates the canvas, sizes it, and then loads the frag. This is important to the shader connecting its resolution to the canvas size. ๐ŸŒฑ

import { FC, useEffect, useRef } from "react";
import GlslCanvas from "glslCanvas";

interface ShaderCanvasProps {
  frag: string;
  setUniforms?: { [key: string]: string };
}

export const ShaderCanvas: FC<ShaderCanvasProps> = (props): JSX.Element => {

  const canvasRef = useRef<HTMLCanvasElement>();
  const containerRef = useRef<HTMLDivElement>();

  const resizer = (
    canvas: HTMLCanvasElement,
    container: HTMLDivElement
  ): void => {
    canvas.width = container.clientWidth + window.devicePixelRatio;
    canvas.height = container.clientHeight + window.devicePixelRatio;
    canvas.style.width = container.clientWidth + "px";
    canvas.style.height = container.clientHeight + "px";
  };

  useEffect(() => {
    const node = canvasRef.current;
    const container = containerRef.current;
    const sandbox = new GlslCanvas(canvasRef.current);
    for (let k in props.setUniforms) {
      sandbox.setUniform(k, props.setUniforms[k]);
    }

    resizer(node, container);
    sandbox.load(props.frag);

    const handler = () => {
      if (
        node.clientWidth !== container.clientWidth ||
        node.clientHeight !== container.clientHeight
      )
        resizer(canvasRef.current, containerRef.current);
    };

    window.addEventListener("resize", handler);

    return () => {
      window.removeEventListener("resize", handler);
    };
  }, []);

  return (
    <div ref={containerRef} style={{ width: "100%", height: "100%" }}>
      <canvas ref={canvasRef}></canvas>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

If you want to see this code alive in the wild, start here and please respond with any ideas for refactoring or optimizing! ๐Ÿ› 

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

๐Ÿ‘‹ Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay