Stacking Shapes

Overview

The Stacking Shapes component is an interactive physics-driven experience built with Matter.js that features draggable brand shapes, a logo sprite, sound effects, and a scoring system. It creates an engaging, playful experience perfect for 404 pages or other interactive sections of the By Default website.

Features

  • Interactive Physics: Draggable shapes with realistic physics simulation
  • Logo Integration: SVG logo as a physics body with custom rendering
  • Sound System: Collision-based audio feedback with user controls
  • Scoring System: Progress tracking with win condition at 100%
  • Responsive Design: Scales appropriately across different screen sizes
  • Accessibility: Keyboard navigation and screen reader support
  • Performance Optimized: Pauses when tab is hidden, cleans up on unload

File Structure

stacking-shapes/
 stacking-shapes.css    - Styling for canvas, modal, and UI elements
 stacking-shapes.js     - Main physics engine implementation
 stacking-shapes-1.js   - Backup version 1
 stacking-shapes-2.js   - Backup version 2

Technical Stack

  • Matter.js 0.20.x - 2D physics engine
  • pathseg polyfill - SVG path compatibility
  • Web Audio API - Sound generation
  • Canvas 2D - Rendering
  • LocalStorage - Sound preference persistence

HTML Structure

Required Elements

<!-- Main container -->
<div id="stacking-shapes-canvas"></div>

<!-- Score HUD -->
<div class="stacking-shapes-score-bar">
  <span id="stacking-shapes-score-fill" style="width:0%"></span>
</div>
<div id="stacking-shapes-score-number">0%</div>
<button id="stacking-shapes-score-reset">Reset</button>

<!-- Sound Controls -->
<input type="checkbox" id="stacking-shapes-sound-toggle" />
<span class="stacking-shapes-voice-control">Sound : On</span>

<!-- Modal -->
<div id="stacking-shapes-media-modal" class="stacking-shapes-modal" aria-hidden="true">
  <div class="stacking-shapes-modal__dialog">
    <button id="stacking-shapes-modal-close">Close</button>
    <div id="stacking-shapes-modal-body"></div>
  </div>
</div>

<!-- SVG Paths (hidden) -->
<svg width="0" height="0" style="position:absolute;left:-9999px;">
  <path id="shape1" d="..." />
  <path id="shape2" d="..." />
  <path id="shape3" d="..." />
  <path id="shape4" d="..." />
  <path id="logo-shape" d="..." />
</svg>

Shape Configuration

Adding New Shapes

  1. Add SVG Path: Create a new <path> element in the hidden SVG
  2. Update JavaScript: Add shape to the shapes array in initHeroPhysics()
// In initStackingShapes() function
window.heroPhysics.shapes = [
  bodyFromPath("shape1", innerWidth * 0.25, innerHeight * 0.25, "#094C45", "one", "#news"),
  bodyFromPath("shape2", innerWidth * 0.5, innerHeight * 0.3, "#F7A3BC", "two", "#founders"),
  bodyFromPath("shape3", innerWidth * 0.75, innerHeight * 0.3, "#FFB533", "three", "#work"),
  bodyFromPath("shape4", innerWidth * 0.15, innerHeight * 0.15, "#88D3CD", "four", "#about"),
  // Add your new shape here
  bodyFromPath("shape5", innerWidth * 0.6, innerHeight * 0.4, "#FF6B6B", "five", "#contact"),
];

Shape Properties

bodyFromPath(id, x, y, fill, label, link)
  • id: SVG path element ID
  • x, y: Initial position coordinates
  • fill: Hex color code (e.g., "#094C45")
  • label: Text displayed on the shape
  • link: Optional URL for click actions

Physics Properties

Shapes can be customized with different physics properties:

{
  restitution: 0.6,        // Bounciness (0-1)
  frictionAir: 0.02,       // Air resistance
  render: { 
    fillStyle: "#094C45",  // Color
    strokeStyle: "transparent"
  }
}

Logo Customization

  1. Update SVG Path: Modify the #logo-shape path element
  2. Update Image Source: Change the logo image URL in addLogoSprite()
// In addLogoSprite() function
logoImage.src = "path/to/your/logo.svg";

Logo Positioning

Adjust logo offset and scaling:

// Logo image offset adjustments
const offsetX = -3;  // Move left/right
const offsetY = -24; // Move up/down

// Original body dimensions (adjust if needed)
const originalBodyWidth = 370;  // Half of viewBox width
const originalBodyHeight = 176; // Half of viewBox height

Sound System

Sound Configuration

Sounds are generated using Web Audio API with configurable parameters:

// In stackingShapesPlayPop() function
const speed = Math.min(Math.max(impact, 0), 10);
const peak = 0.01 + 0.02 * (speed / 10);  // Volume
const pitch = 100 + 10 * speed;           // Frequency

Sound Triggers

  • Collision Speed: Minimum 2.0 units for sound
  • Cooldown: 300ms between sounds per body
  • User Interaction: First gesture enables audio context

Customizing Sound

To modify sound characteristics:

// In stackingShapesPlayPop() function
osc.type = "triangle";  // Wave type: sine, square, triangle, sawtooth
gain.gain.exponentialRampToValueAtTime(peak, t + 0.01);  // Attack
gain.gain.exponentialRampToValueAtTime(0.0001, t + 0.16); // Decay

Scoring System

How Scoring Works

  • Collision-based: Only shape-to-shape collisions score
  • Speed threshold: Minimum 2.2 units relative velocity
  • Cooldown: 600ms per collision pair
  • Interaction window: 4 seconds after user interaction
  • Win condition: 100% score triggers victory modal

Score Configuration

// In initScoreSystem() function
const MIN_SPEED_FOR_SCORE = 2.2;    // Minimum collision speed
const SCORE_COOLDOWN_MS = 600;      // Time between scores
const inc = Math.min(2, 0.2 + speed * 0.2); // Score increment

Responsive Design

Scaling System

The hero uses a responsive scaling system based on a 1280x800 base:

const BASE_W = 1280, BASE_H = 800;

function getResponsiveScale() {
  const s = Math.min(innerWidth / BASE_W, innerHeight / BASE_H);
  return Math.max(0.45, Math.min(1.2, s)); // Min 45%, Max 120%
}

Responsive Behavior

  • Shapes scale uniformly with screen size
  • Walls rebuild on resize
  • Shapes reposition within safe margins
  • Logo scales proportionally

Mobile Considerations

  • Touch support: Drag shapes with touch
  • Scroll prevention: touch-action: pan-y allows vertical scroll
  • Performance: Pauses when tab hidden

Accessibility Features

Keyboard Navigation

  • Voice control: Space/Enter to toggle sound
  • Modal escape: Escape key closes modals
  • Focus management: Proper focus handling

Screen Reader Support

<!-- Score announcements -->
<div class="stacking-shapes-score-bar" aria-live="polite">
  <span id="stacking-shapes-score-fill"></span>
</div>

<!-- Modal accessibility -->
<div id="stacking-shapes-media-modal" aria-hidden="true">
  <button id="stacking-shapes-modal-close" aria-label="Close">Close</button>
</div>

Performance Optimization

Lifecycle Management

  • Initialization: Only runs once, guards against double-init
  • Pause/Resume: Stops when tab hidden, resumes when visible
  • Cleanup: Properly destroys engine on page unload

Memory Management

// Cleanup function
function cleanupStackingShapes() {
  if (window.stackingShapes.engine) {
    Matter.Runner.stop(window.stackingShapes.runner);
    Matter.Render.stop(window.stackingShapes.render);
    Matter.Engine.clear(window.stackingShapes.engine);
    // Reset all references
  }
}

Performance Settings

// Fixed timestep for consistent physics
window.stackingShapes.runner = Runner.create({ 
  isFixed: true, 
  delta: 1000/60  // 60 FPS
});

Troubleshooting

Common Issues

Shapes not appearing:

  • Check SVG paths exist with correct IDs
  • Verify Matter.js library loaded
  • Check browser console for errors

Sound not working:

  • Ensure user has interacted with page
  • Check sound toggle state
  • Verify Web Audio API support

Performance issues:

  • Check if tab is hidden (physics pause)
  • Monitor memory usage
  • Verify cleanup on navigation

Responsive problems:

  • Check viewport meta tag
  • Verify canvas resize handling
  • Test on different screen sizes

Integration with Webflow

Webflow-Specific Considerations

  • Modal content: Webflow handles modal content management
  • Responsive breakpoints: Align with Webflow's responsive system
  • Asset management: Use Webflow's asset URLs for images
  • Custom code: Place in Webflow's custom code sections

Deployment Checklist

  • Matter.js and pathseg libraries loaded
  • All required HTML elements present
  • SVG paths properly defined
  • CSS custom properties set
  • Sound toggle functional
  • Modal system working
  • Responsive scaling tested
  • Accessibility features verified

Was this page helpful?

We use this feedback to improve our documentation.

Thanks for your feedback