Hi, I'm Lylia!

Full-stack web developer and web designer

Crafting Dynamic Hero Sections with Nuxt and P5.js

user

Learn how to seamlessly integrate p5.js into your Nuxt project to create captivating hero sections.

Jan 28, 2025


Table of Content


Today, I will show you how to integrate p5.js in your next Nuxt project and demonstrate creative ways to use P5 in your projects.

Installing P5.js

First, you’ll have to install p5 using your favorite package manager:

npm install p5

Create your Nuxt-p5 component

We will then create a component in which we will set up p5.js and use it later in our project.

// components/nuxt-p5.vue
<template>
    <div ref="canvasContainer"></div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";

// Define props for the component
const props = defineProps({
  sketch: {
    type: Function,
    required: true,
  },
});

const canvasContainer = ref(null); // Reference for the canvas container
defineExpose(['canvasContainer']); // Expose the canvas container for potential advanced use cases
let p5Instance = null; // Variable to hold the p5 instance

// Lifecycle hook: Mounting the component
onMounted(async () => {
  const { default: P5 } = await import("p5");
  p5Instance = new P5(props.sketch, canvasContainer.value); // Initialize the p5 instance with the provided sketch
});

// Lifecycle hook: Cleaning up the p5 instance before unmounting
onBeforeUnmount(() => {
  if (p5Instance) {
    p5Instance.remove(); // Remove the p5 instance to free resources
  }
});
</script>

Using the component

We are now ready to add P5.js to our components. As an example, we will build a dynamic hero section.

Building a dynamic hero section

Let’s create a hero section that combines text and a visually appealing animated background powered by p5.js. The following code achieves this:

<template>
  <section class="font-sans relative min-h-screen overflow-hidden">
    <!-- Include the NuxtP5 component with the sketch prop -->
    <NuxtP5 :sketch="heroSketch" />

    <!-- Overlay text content -->
    <div
      class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-center text-white z-10"
    >
      <h1 class="text-5xl mb-6">Welcome to Dynamic Vue</h1>
      <p class="text-3xl mb-4">
        Build stunning and interactive web experiences with ease.
      </p>
      <button
        class="bg-blue-700 p-4 rounded-sm hover:bg-blue-500 transition-all transition-ease"
      >
        Get Started
      </button>
    </div>
  </section>
</template>

<script setup>
// Sketch function for the hero section background
const heroSketch = (p5) => {
  let particles = []; // Array to hold the particles

  // Setup function: Initialize the canvas and particles
  p5.setup = () => {
    p5.createCanvas(window.innerWidth, window.innerHeight); // Fullscreen canvas
    for (let i = 0; i < 100; i++) {
      particles.push(new Particle(p5)); // Generate particles
    }
  };

  // Handle window resizing to ensure the canvas adjusts
  p5.windowResized = () => {
    p5.resizeCanvas(p5.windowWidth, p5.windowHeight);
  };

  // Draw function: Called every frame to update and render the particles
  p5.draw = () => {
    p5.background(20, 20, 40); // Deep blue background
    for (let particle of particles) {
      particle.update(); // Update particle positions
      particle.show();   // Render the particles
    }
  };

  // Particle class
  class Particle {
    constructor(p5) {
      this.p5 = p5;
      this.pos = p5.createVector(p5.random(p5.width), p5.random(p5.height)); // Random initial position
      this.vel = p5.createVector(p5.random(-1, 1), p5.random(-1, 1)); // Random velocity
      this.radius = p5.random(2, 2); // Fixed particle size
    }

    // Update the particle's position and handle boundary collisions
    update() {
      this.pos.add(this.vel);
      if (this.pos.x > this.p5.width || this.pos.x < 0) {
        this.vel.x *= -1;
      }
      if (this.pos.y > this.p5.height || this.pos.y < 0) {
        this.vel.y *= -1;
      }
    }

    // Render the particle
    show() {
      this.p5.noStroke();
      this.p5.fill(100, 200, 255, 150); // Soft blue particle color
      this.p5.ellipse(this.pos.x, this.pos.y, this.radius);
    }
  }
};
</script>

Explanation of the code

  1. NuxtP5 component:
    • This component is a reusable wrapper for integrating p5.js sketches into your Nuxt app.
    • It uses Vue’s onMounted lifecycle hook to dynamically import the p5.js library and initialize a sketch.
    • The onBeforeUnmount hook ensures proper cleanup of resources by removing the p5 instance.
  2. Dynamic hero section:
    • We use the NuxtP5 component and pass the heroSketch function as a prop.
    • The sketch creates a dynamic animated background with moving particles.
    • The particles are lightweight objects that bounce within the canvas, creating a visually pleasing effect.
  3. Hero content overlay:
    • The div containing the hero content is styled using TailwindCSS.
    • The absolute positioning ensures the text is centered on top of the animated background.

Conclusion

By integrating p5.js into your Nuxt project, you can create stunning, interactive experiences like this dynamic hero section. The modularity of the NuxtP5 component allows you to reuse it for any other p5.js-based visuals in your app. Experiment with different sketches to bring your unique vision to life!

Say Hi

I'm a freelance fullstack developer, working with Vue, Svelte, TypeScript, and Headless CMSes.
I'm interested in AI, web development, and creative coding. I love helping my clients achieve their goals by focusing on accessibility and using technology to empower all people.
Feel free to reach out to me here.