Setting up p5.js in Nuxt

2020-08-12

I have recently been watching a lot of videos from the excellent channel The Coding Train, which inspired me to start playing with p5.js, a JavaScript library for drawing. p5 has a really nice online editor, which you can use to create and host projects, but I wanted to keep mine on this website, with everything else.

You can use p5 on a site just by adding a script tag to a CDN with the library, but as I’ve built this site with Vue and Nuxt, I opted for using vue-p5, which provides p5 as a Vue component you can add. This works great in a normal Vue page, but breaks with Nuxt’s server-side rendering, as p5 tries to access the window object, which doesn’t exist server-side.

To fix this issue, you have to implement two simple fixes. First, have a component where you import the vue-p5 component normally, like so:

<template>
  <vue-p5 v-on="{setup, draw}"></vue-p5>
</template>

<script lang="ts">
  import Vue from "vue";
  import * as p5 from "p5";

  export default Vue.extend({
    components: {
      VueP5: () => import("vue-p5"),
    },
    methods: {
      setup(sketch: p5) {
        sketch.resizeCanvas(800, 600);
        sketch.background('green');
        sketch.text('Hello p5!', 20, 20);
      }
    }
  });
</script>

Now, you can go to the page component and import my-p5-component. The important aspect is to first, wrap the component in a <client-only> tag, and second, import it in a lazy loaded manner:

<template>
  <div>
    <h1>p5 Example</h1>
    <client-only>
      <myp5component />
    </client-only>
  </div>
</template>

<script lang="ts">
  import Vue from "vue";

  export default Vue.extend({
    components: {
      myp5component: () => {
        if (process.client) {
          return import("@/components/my-p5-component.vue");
        }
      },
    },
  });
</script>

Given that we’re checking for process.client and the component is inside a <client-only> tag, it won’t be imported until we’re client side, where window is defined! With that, our issue is solved.

It’s important to note, that you lose the benefits of server-side rendering for this component, so I recommend only keeping the p5 canvas there, and to leave the rest of the content you want on the page outside of the <client-only> tag. You can read more about it here.

Using plugins

If you prefer to register your components using Plugins, you can declare your plugin as client-side only, like it’s explained here, by changing the plugin’s file extension to .client.js.

How to use p5

With this, we now have a way of running a simple p5 sketch in a Nuxt website, but if you start playing around with it, you will realize that there are some roadblocks still to clear.

First, to use Typescript, you have to type the sketch object that is passed as a variable to the setup and draw functions. This is easy, you just have to import p5 using import * as p5 from "p5";, which allows you to use p5 as a type for objects, like we did in the first code example in this post.

Now, with TypeScript out of the way, the next problem arises when trying to use functions that are usually on the global scope when you use p5 outside of Nuxt. To use them, you have to make use of the sketch: p5 object that is passed to the setup and draw functions. You can call all the p5 functions you want with this object, for example sketch.createVector to create a vector or sketch.frameRate() to get the last frame’s framerate.

Lastly, to make use of p5 classes like Vector, you can use them with p5.Vector.

To see an example of p5 in action in a Nuxt website, check out an Experiment I built recently here. If there’s anything else you’re having trouble with, let me know and I’ll try to help you out!