Copied SVG to clipboard
Something went wrong
Copied code to clipboard
Something went wrong

Default

User image

Default

Name

  • Osmo Discount
    -25%
The Vault/

Interactive Pixel Grid

Interactive Pixel Grid

Documentation

Webflow

Code

Setup: External Scripts

External Scripts in Webflow

Make sure to always put the External Scripts before the Javascript step of the resource.

In this video you learn where to put these in your Webflow project? Or how to include a paid GSAP Club plugin in your project?

HTML

Copy
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.7/dist/gsap.min.js"></script>

Step 1: Copy structure to Webflow

Copy structure to Webflow

In the video below we described how you can copy + paste the structure of this resource to your Webflow project.

Copy to Webflow

Webflow structure is not required for this resource.

Step 1: Add HTML

HTML

Copy
<div 
  class="bg"
  data-grid=""	
  data-grid-size-desktop="20"
  data-grid-size-mobile="8"
  data-grid-background="#FFFFFF"
  data-grid-border-size="2"
  data-grid-border-color="rgba(0, 0, 0, 0.2)"
  data-grid-colors="[#C5D4FF, #B7B0FF, #FF5FCE, #4136FF, #FFF751,  #87FEFF, #C4FF3F]"
></div>

HTML structure is not required for this resource.

Step 2: Add CSS

CSS

Copy
.grid-bg {
  position: absolute;
  inset: 0%;
  z-index: 0;
}

Step 2: Add custom Javascript

Custom Javascript in Webflow

In this video, Ilja gives you some guidance about using JavaScript in Webflow:

Step 2: Add Javascript

Step 3: Add Javascript

Javascript

Copy
function debounce(func, wait) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), wait);
  }
}

function initGrid(el) {
  // Define default values that are used if no attributes other than [data-grid] are used
  const defaults = {
    gridBackground: "#FFFFFF",
    gridSizeDesktop: 20,
    gridSizeMobile: 8,
    gridBorderSize: 2,
    gridBorderColor: "rgba(0, 0, 0, 0.2)",
    gridColors: ["#C5D4FF", "#B7B0FF", "#FF5FCE", "#4136FF", "#FFF751", "#87FEFF", "#C4FF3F"]
  };

  const gridBackground = el.getAttribute("data-grid-background") || defaults.gridBackground;
  const gridSizeDesktop = parseInt(el.getAttribute("data-grid-size-desktop")) || defaults.gridSizeDesktop;
  const gridSizeMobile = parseInt(el.getAttribute("data-grid-size-mobile")) || defaults.gridSizeMobile;
  const gridBorderSize = parseFloat(el.getAttribute("data-grid-border-size")) || defaults.gridBorderSize;
  const gridBorderColor = el.getAttribute("data-grid-border-color") || defaults.gridBorderColor;
  
  // Parse grid colors so you can use HEX or RGBA values in the attribute
  let gridColors = defaults.gridColors;
  const attrColors = el.getAttribute("data-grid-colors");
  if (attrColors) {
    try {
      gridColors = JSON.parse(attrColors);
    } catch (e) {
      try {
        gridColors = JSON.parse(attrColors.replace(/'/g, '"'));
      } catch (e2) {
        gridColors = defaults.gridColors;
      }
    }
  }


  el.style.backgroundColor = gridBackground;
  const canvas = document.createElement("canvas");
  el.appendChild(canvas);
  const ctx = canvas.getContext("2d");

  let cols, rows, squareSize, blocks, lastHoveredIndex = null;
  
  // Generate the actual grid
  function setupGrid() {
    canvas.width = el.offsetWidth;
    canvas.height = el.offsetHeight;
    cols = (window.innerWidth < 768) ? gridSizeMobile : gridSizeDesktop;
    squareSize = canvas.width / cols;
    rows = Math.ceil(canvas.height / squareSize);
    blocks = [];
    for (let y = 0; y < rows; y++) {
      for (let x = 0; x < cols; x++) {
        blocks.push({ x: x * squareSize, y: y * squareSize, color: "white", alpha: 0 });
      }
    }
  }
  
  // Draw the squares
  function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    blocks.forEach(block => {
      ctx.fillStyle = block.color;
      ctx.globalAlpha = block.alpha;
      ctx.fillRect(block.x, block.y, squareSize, squareSize);
      ctx.globalAlpha = 1;
      ctx.strokeStyle = gridBorderColor;
      ctx.lineWidth = gridBorderSize;
      ctx.strokeRect(block.x, block.y, squareSize, squareSize);
    });
    requestAnimationFrame(draw);
  }

  // Define how long it takes for blocks to fade out
  function fadeOut(block) {
    gsap.to(block, { alpha: 0, duration: 2, delay: 0.5 });
  }

  function supportsTouch() {
    return "ontouchstart" in window || navigator.maxTouchPoints;
  }

  // Init mousemove listener if we're NOT on a touchscreen
  if (!supportsTouch()) {
    canvas.addEventListener("mousemove", (event) => {
      const rect = canvas.getBoundingClientRect();
      const mouseX = event.clientX - rect.left;
      const mouseY = event.clientY - rect.top;
      const hoveredIndex = blocks.findIndex(block =>
        mouseX >= block.x &&
        mouseX < block.x + squareSize &&
        mouseY >= block.y &&
        mouseY < block.y + squareSize
      );
      if (hoveredIndex !== -1 && hoveredIndex !== lastHoveredIndex) {
        const block = blocks[hoveredIndex];
        block.color = gridColors[Math.floor(Math.random() * gridColors.length)];
        
        // Define duration of fade in animation
        gsap.to(block, { alpha: 1, duration: 0.1, overwrite:true });
        
        // Start fade out
        fadeOut(block);
        lastHoveredIndex = hoveredIndex;
      }
    });
  }

  window.addEventListener("resize", debounce(setupGrid, 200));

  setupGrid();
  draw();
}

function initGrids() {
  document.querySelectorAll("[data-grid]").forEach(el => initGrid(el));
}

document.addEventListener("DOMContentLoaded", () => {
  initGrids();
});

Step 3: Add custom CSS

Step 2: Add custom CSS

Custom CSS in Webflow

Curious about where to put custom CSS in Webflow? Ilja explains it in the below video:

CSS

Copy

Implementation

This script is setup to be as flexible as possible. In essence, all you need is a div with [data-grid] attribute. All of the attributes (also listed below) are optional overrides of the default values listed in the JavaScript. So imagine you want to have the same grid all over your website, just change the default values, and you don't have to include any extra variables on your data-grid elements.

Default options:

const defaults = { 
  gridBackground: "#FFFFFF", // Background color of the canvas
  gridSizeDesktop: 20, // Amount of blocks in a row for desktop
  gridSizeMobile: 8, // Amount of blocks for screens < 768px
  gridBorderSize: 2, // Stroke width of the blocks
  gridBorderColor: "rgba(0, 0, 0, 0.2)", // Stroke color
  
  // Aray with default color options for hover
  gridColors: ["#C5D4FF", "#B7B0FF", "#FF5FCE", "#4136FF", "#FFF751", "#87FEFF", "#C4FF3F"] 
};

Available HTML attributes:

data-grid="" → The main selector, not optional

data-grid-size-desktop="20" → Controls the amount of blocks that will fit in a row.

data-grid-size-mobile="8" → Same as above, but for when the screen is smaller than 768px.

data-grid-background="#FFFFFF" → Set the background color of the canvas, can be 'transparent' too.

data-grid-border-size="2" → Set the stroke width of the blocks.

data-grid-border-color="rgba(0, 0, 0, 0.2)" → Set the color of the stroke on all the blocks.

data-grid-colors="[#C5D4FF, #B7B0FF, #FF5FCE, #4136FF, #FFF751, #87FEFF, #C4FF3F]" → An array of colors that are randomized on hover. This can be rgba values too, just wrap each color in between ' ' like so: ['rgba(0,0,0,0.1)', 'rgba(0,0,0,0.2)'].

Resource Details

Advanced
Background
Code
Sections
VanillaJS

Original source

Ilja van Eck

Creator Credits

We always strive to credit creators as accurately as possible. While similar concepts might appear online, we aim to provide proper and respectful attribution.