Embedding GLSL shader in hugo

Prakash

Having recently got into the wild world of glsl shaders, I wanted to experiment if I could get glsl shaders rendering in my hugo blog.

It was surprisingly easier that I expected it to be. The centre piece of all the heavy lifter for this purpose is the javascript library GlslCanvas

You simply create a canvas tag like so

<canvas class="glslCanvas"
   data-fragment="<the shader code>">
</canvas>

And include the library like so

<script src="https://cdn.jsdelivr.net/npm/glslCanvas@0.2.6/dist/GlslCanvas.js"></script>

We can automate this whole process by creating a hugo shortcode

{{ $path := .Get "src" }}
{{ $file := resources.Get $path }}

{{ if $file }}
  {{ $height := .Get "height" | default "400px" }}
  <div class="shader-wrapper" style="width: 100%; margin: 1.5rem 0;">
    <canvas class="glslCanvas" data-fragment="{{ $file.Content }}" style="width: 100%; height: {{ $height }}; border-radius: 8px; display: block;"></canvas>
  </div>
<script src="https://cdn.jsdelivr.net/npm/glslCanvas@0.2.6/dist/GlslCanvas.js"></script>

<script>
  const canvas = document.querySelector(".glslCanvas");

  if (typeof GlslCanvas === "undefined") {
    console.error("GlslCanvas failed to load");
  } else {
    new GlslCanvas(canvas);
  }
</script>
{{ else }}
<p style="color: red;">Shader file not found: {{ $path }} and file {{ $file }}</p>
{{ end }}

This shortcode can be used like so

{{ < add-glsl src="shaders/test.frag" height="400px" >}}

With the file

HUGO_ROOT
    assets
    └── shaders
        └── test.frag

Where the content of test.frag is

precision mediump float;

uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_mouse;

void main() {
    vec2 st = (gl_FragCoord.xy*2.0 - 1.0) / u_resolution.xy;
    
    vec3 color = vec3(
        st.x * abs(sin(u_time * 0.5)),
        st.y * abs(cos(u_time * 0.7)),
        abs(sin(u_time))
    );

    gl_FragColor = vec4(color, 1.0);
}

Will result in

Is this not awesome?