🐞 SWBug Class Reference

Animated moving points with random walk and Perlin noise behaviors — SketchWaveJS Stage

Overview

SWBug is a SketchWave class that creates animated "bug" objects capable of autonomous movement across a canvas. Bugs can move using either random walk or Perlin noise algorithms, creating distinct visual patterns and behaviors.

Key Features:

  • Extends SWPoint, inheriting all point functionality
  • Two movement modes: random walk ('r') and Perlin noise ('p')
  • Configurable speed, trail persistence, and boundary wrapping
  • Smooth trail rendering with automatic wrap-around handling
  • Integration with SWGrid for user coordinate mapping
  • Distance tracking from original position

Understanding Movement Modes

Random Walk ('r' mode)

Random walk movement creates completely unpredictable, erratic behavior. At each step, the bug:

  1. Chooses a random direction (0 to 360 degrees)
  2. Moves by its speed value in that direction
  3. Repeats with a completely new random direction on the next frame

This creates a jittery, zigzag pattern with sudden direction changes. It's excellent for simulating:

  • Brownian motion or molecular movement
  • Confused or startled behavior
  • Chaotic, unpredictable patterns
  • Mathematical random walk processes

Perlin Noise ('p' mode)

Perlin noise movement creates smooth, organic, natural-looking paths. Named after Ken Perlin who developed it for the movie Tron, Perlin noise generates values that change gradually and continuously over time. The bug:

📖 New to Perlin noise? Check out our Perlin Noise Tutorial — a step-by-step guide with live interactive demos, perfect for understanding how and why it works.
  1. Samples Perlin noise functions for X and Y directions
  2. Uses these smoothly-changing values to determine movement direction
  3. Creates flowing, wandering paths that look natural

The noiseAmount parameter controls how quickly the noise values change:

  • Small values (0.001 - 0.01): Very smooth, gentle curves and lazy wandering
  • Medium values (0.01 - 0.1): Moderate curves, balanced exploration
  • Large values (0.1+): More erratic but still continuous, approaching random-walk behavior

Perlin noise is perfect for simulating:

  • Natural creature movement (insects, fish, birds)
  • Organic, flowing patterns
  • Realistic wandering behavior
  • Terrain generation and natural phenomena

Comparison: Random Walk vs. Perlin Noise

Aspect Random Walk ('r') Perlin Noise ('p')
Direction Change Completely random each frame Smooth, continuous transitions
Path Appearance Jagged, zigzag, erratic Flowing, curved, organic
Predictability Impossible to predict next move Somewhat predictable, smooth curves
Performance Faster (simple calculation) Slightly slower (noise function calls)
Natural Feel Chaotic, artificial Organic, lifelike
Best For Randomness, chaos, confusion Natural movement, exploration
Configuration Speed only Speed + noiseAmount
💡 Tip: Use toggleMode() to switch between random walk and Perlin noise during runtime, creating dynamic behavior changes. For example, a bug could wander smoothly (Perlin) until it encounters an obstacle, then switch to random walk (panic mode) before returning to smooth movement.

Constructor

new SWBug(x, y, mode = 'r', speed = 1, strokeColor = undefined, options = {})

Parameters

Parameter Type Default Description
x number required Initial X coordinate in user space
y number required Initial Y coordinate in user space
mode 'r' | 'p' 'r' 'r' for random walk, 'p' for Perlin noise
speed number 1 Movement speed per frame (step size)
strokeColor SWColor undefined Color for the bug (SWColor instance)
options Object {} Optional configuration object (see below)

Options Object Properties

Property Type Default Description
noiseAmount number 0.01 Controls Perlin noise smoothness; smaller = smoother paths, larger = more erratic
strokeWeight number 5 Thickness of the bug's point when drawn
trailPersistence number 50 Number of previous positions to keep in trail history
shouldWrapAround boolean true If true, bug wraps to opposite edge when leaving grid bounds

Example Usage

// Simple random walk bug
let bug1 = new SWBug(0, 0);

// Perlin noise bug with custom speed and color
let bug2 = new SWBug(5, 5, 'p', 0.5, swBlue);

// Fully configured bug
let bug3 = new SWBug(0, 0, 'p', 1, swRed, {
    noiseAmount: 0.005,    // Very smooth movement
    strokeWeight: 10,       // Larger point
    trailPersistence: 100,  // Longer trail
    shouldWrapAround: false // Don't wrap at edges
});

Properties

mode

Type: 'r' | 'p'

Current movement mode: 'r' for random walk, 'p' for Perlin noise. Can be changed at runtime using setMode() or toggleMode().

speed

Type: number

Movement speed per frame. Higher values = faster movement. Can be changed using setSpeed().

noiseAmount

Type: number (default: 0.01)

Controls how quickly Perlin noise values change. Only affects 'p' mode. Smaller values create smoother, more gradual curves.

origin

Type: {x: number, y: number}

Starting position of the bug, stored for reference. Used to calculate distanceFromOriginal.

shouldShowTrails

Type: boolean (default: false)

Whether to display the bug's movement trail. Enable with setShouldShowTrails(true).

trail

Type: Array of {x: number, y: number, break: boolean}

Array storing previous positions. The break property marks discontinuities (e.g., when wrapping around edges).

trailPersistence

Type: number (default: 50)

Maximum number of positions stored in trail. Older positions are removed automatically. Change with setTrailPersistence().

shouldWrapAround

Type: boolean (default: true)

If true, bug wraps to opposite edge when leaving grid bounds. If false, bug can move beyond grid. Change with setShouldWrapAround().

Inherited Properties from SWPoint

SWBug inherits all properties from SWPoint, including:

  • x, y, z - Position coordinates
  • strokeWeight - Point thickness
  • strokeColor - SWColor instance for color
  • penOn, trail, maxTrailLength - SWPoint trail properties (separate from SWBug's trail system)

Methods

Movement Methods

move(grid)

Parameters: grid (SWGrid, optional) - Grid for wrap-around bounds

Updates the bug's position based on its mode. If shouldWrapAround is true and a grid is provided, wraps the bug to the opposite edge when it leaves grid bounds. Automatically manages trail if enabled.

// In draw() loop
bug.move(grid);  // Move and wrap within grid
bug.drawOnGrid(grid);

Rendering Methods

drawOnGrid(grid)

Parameters: grid (SWGrid) - Grid for coordinate mapping

Draws the bug and its trail (if enabled) on the grid. Overrides SWPoint's drawOnGrid() to include trail rendering. Trails are drawn as connected line segments with breaks at wrap-around points.

function draw() {
    background(220);
    grid.draw();
    bug.drawOnGrid(grid);  // Draws both trail and bug
}

Configuration Methods

setSpeed(newSpeed)

Parameters: newSpeed (number)

Changes the bug's movement speed. Affects both random walk and Perlin noise modes.

bug.setSpeed(2);  // Double the speed
bug.setSpeed(0.5);  // Half speed

setMode(newMode)

Parameters: newMode ('r' | 'p')

Sets the bug's movement mode to random walk ('r') or Perlin noise ('p').

bug.setMode('p');  // Switch to Perlin noise
bug.setMode('r');  // Switch to random walk

toggleMode()

Parameters: None

Toggles between random walk and Perlin noise modes. If currently 'r', switches to 'p', and vice versa.

// Toggle on click
function mousePressed() {
    bug.toggleMode();
}

setShouldShowTrails(show)

Parameters: show (boolean)

Enables or disables trail rendering. When disabled, trail array is cleared.

bug.setShouldShowTrails(true);   // Show trail
bug.setShouldShowTrails(false);  // Hide trail

setTrailPersistence(n)

Parameters: n (number)

Sets the maximum number of positions to keep in the trail. If current trail is longer, it's trimmed to the last n positions.

bug.setTrailPersistence(100);  // Long trail
bug.setTrailPersistence(20);   // Short trail

setShouldWrapAround(shouldWrap)

Parameters: shouldWrap (boolean)

Controls whether the bug wraps to the opposite edge when leaving grid bounds.

bug.setShouldWrapAround(true);   // Enable wrapping
bug.setShouldWrapAround(false);  // Disable wrapping

Getters

distanceFromOriginal

Returns: number

Getter property that calculates the Euclidean distance from the bug's current position to its starting position.

let dist = bug.distanceFromOriginal;
if (dist > 10) {
    console.log("Bug has wandered far!");
}

toString()

Returns: string

Returns a formatted string representation of the bug's state, including position, mode, speed, and distance from origin.

console.log(bug.toString());
// Output: "SWBug(x: 3.45, y: -2.17, mode: p, speed: 1, distFromOrigin: 4.08)"

Inherited Methods from SWPoint

SWBug inherits all methods from SWPoint, including:

  • draw() - Draw in screen coordinates
  • move(dx, dy, dz) - Move by delta values (note: different from SWBug's move(grid))
  • setPen(on), setMaxTrailLength(n), clearTrail() - SWPoint trail methods
  • setStrokeColor(swColor), setStrokeWeight(w) - Style methods
  • distanceTo(otherSWPt) - Calculate distance to another point

Usage Examples

Example 1: Basic Random Walk Bug

let grid;
let bug;

function setup() {
    createCanvas(400, 400);
    
    // Initialize grid
    grid = new SWGrid({UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10)});
    
    // Create a red bug at origin with random walk
    bug = new SWBug(0, 0, 'r', 0.5, swRed);
    bug.setShouldShowTrails(true);
    
    frameRate(30);
}

function draw() {
    background(220);
    grid.draw();
    
    bug.move(grid);
    bug.drawOnGrid(grid);
}

Example 2: Smooth Perlin Noise Bug

let grid;
let bug;

function setup() {
    createCanvas(400, 400);
    
    grid = new SWGrid({UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10)});
    
    // Create a blue bug with very smooth Perlin movement
    bug = new SWBug(0, 0, 'p', 0.3, swBlue, {
        noiseAmount: 0.005,     // Very smooth
        strokeWeight: 8,
        trailPersistence: 150
    });
    
    bug.setShouldShowTrails(true);
    frameRate(30);
}

function draw() {
    background(220);
    grid.draw();
    
    bug.move(grid);
    bug.drawOnGrid(grid);
    
    // Display distance traveled
    fill(0);
    text(`Distance: ${bug.distanceFromOriginal.toFixed(2)}`, 10, 20);
}

Example 3: Multiple Bugs with Different Behaviors

let grid;
let bugs = [];

function setup() {
    createCanvas(600, 600);
    
    grid = new SWGrid({UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10)});
    
    // Create multiple bugs with different behaviors
    let bug1 = new SWBug(-5, 5, 'r', 0.5, swRed);     // Fast random walk
    let bug2 = new SWBug(5, 5, 'p', 0.3, swBlue, {noiseAmount: 0.01});   // Medium Perlin
    let bug3 = new SWBug(-5, -5, 'p', 0.2, swGreen, {noiseAmount: 0.003}); // Slow smooth
    let bug4 = new SWBug(5, -5, 'r', 0.3, swOrange);  // Medium random walk
    
    bugs = [bug1, bug2, bug3, bug4];
    
    // Enable trails for all
    bugs.forEach(bug => {
        bug.setShouldShowTrails(true);
        bug.setTrailPersistence(80);
    });
    
    frameRate(30);
}

function draw() {
    background(220);
    grid.draw();
    
    // Update and draw all bugs
    bugs.forEach(bug => {
        bug.move(grid);
        bug.drawOnGrid(grid);
    });
}

Example 4: Interactive Mode Toggle

let grid;
let bug;

function setup() {
    createCanvas(400, 400);
    
    grid = new SWGrid({UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10)});
    
    bug = new SWBug(0, 0, 'p', 0.5, swPurple, {
        noiseAmount: 0.01,
        trailPersistence: 100
    });
    
    bug.setShouldShowTrails(true);
    frameRate(30);
}

function draw() {
    background(220);
    grid.draw();
    
    bug.move(grid);
    bug.drawOnGrid(grid);
    
    // Display current mode
    fill(0);
    textSize(16);
    text(`Mode: ${bug.mode === 'r' ? 'Random Walk' : 'Perlin Noise'}`, 10, 20);
    text('Click to toggle mode', 10, 40);
}

function mousePressed() {
    bug.toggleMode();
    console.log(`Switched to ${bug.mode} mode`);
}

Example 5: Proximity Detection and Behavior Change

let grid;
let bug;
let target;
const PROXIMITY_THRESHOLD = 2;

function setup() {
    createCanvas(400, 400);
    
    grid = new SWGrid({UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10)});
    
    // Bug starts in smooth Perlin mode
    bug = new SWBug(-5, -5, 'p', 0.4, swBlue, {noiseAmount: 0.01});
    bug.setShouldShowTrails(true);
    
    // Create a target point
    target = new SWPoint(5, 5, undefined, 15, swRed);
    
    frameRate(30);
}

function draw() {
    background(220);
    grid.draw();
    
    // Draw target
    target.drawOnGrid(grid);
    
    // Check proximity to target
    let distance = bug.distanceTo(target);
    
    if (distance < PROXIMITY_THRESHOLD) {
        // Too close! Switch to erratic random walk (panic mode)
        if (bug.mode !== 'r') {
            bug.setMode('r');
            bug.setSpeed(0.7);  // Speed up
        }
    } else {
        // Safe distance, use smooth Perlin movement
        if (bug.mode !== 'p') {
            bug.setMode('p');
            bug.setSpeed(0.4);  // Slow down
        }
    }
    
    bug.move(grid);
    bug.drawOnGrid(grid);
    
    // Display info
    fill(0);
    text(`Distance: ${distance.toFixed(2)}`, 10, 20);
    text(`Mode: ${bug.mode === 'r' ? 'PANIC (Random)' : 'Calm (Perlin)'}`, 10, 40);
}

Example 6: Dynamic Trail Effects

let grid;
let bug;
let trailLength = 50;

function setup() {
    createCanvas(400, 400);
    
    grid = new SWGrid({UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10)});
    
    bug = new SWBug(0, 0, 'p', 0.5, swGreen, {
        noiseAmount: 0.008,
        trailPersistence: trailLength
    });
    
    bug.setShouldShowTrails(true);
    frameRate(30);
}

function draw() {
    background(220);
    grid.draw();
    
    // Vary trail length over time using sine wave
    trailLength = int(50 + 50 * sin(frameCount * 0.02));
    bug.setTrailPersistence(trailLength);
    
    bug.move(grid);
    bug.drawOnGrid(grid);
    
    // Display info
    fill(0);
    text(`Trail Length: ${trailLength}`, 10, 20);
}

function keyPressed() {
    if (key === ' ') {
        // Toggle trail on spacebar
        bug.setShouldShowTrails(!bug.shouldShowTrails);
    }
}

Best Practices

Performance Tips

  • Limit trailPersistence to reasonable values (50-200) for better performance
  • Random walk mode is faster than Perlin noise mode
  • Use setShouldShowTrails(false) when trails aren't needed
  • For many bugs, consider updating only visible ones

Design Tips

  • Choose noiseAmount carefully: 0.001-0.01 for smooth, 0.05+ for erratic
  • Match speed to your canvas size and frame rate
  • Use contrasting colors for multiple bugs to distinguish them
  • Combine mode changes with other events (proximity, time, user input) for dynamic behavior
  • Consider the visual: Random walk = chaos, Perlin = nature

Common Pitfalls

  • Forgetting to call move(grid) in the draw loop - bug won't move
  • Setting speed too high - bug may skip across the grid
  • Not providing a grid to move() when shouldWrapAround is true - wrapping won't work
  • Confusing SWBug's trail system with SWPoint's pen trail - they're separate
  • Setting noiseAmount too high (>0.1) - loses the benefit of Perlin smoothness

Integration with SketchWave Classes

Required Dependencies

To use SWBug, you must include these scripts in order:

<!-- p5.js library -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.js"></script>

<!-- SketchWave classes in dependency order -->
<script src="scripts/swColor.js"></script>
<script src="scripts/swPoint.js"></script>
<script src="scripts/swGrid.js"></script>
<script src="scripts/swBug.js"></script>

<!-- Your sketch -->
<script src="scripts/yourSketch.js"></script>

Working with SWGrid

SWBug is designed to work seamlessly with SWGrid:

  • Bug coordinates are in user space (grid coordinates)
  • Grid handles conversion to screen pixels for rendering
  • Wrapping uses grid bounds (UL and LR corners)
  • Always pass the grid to move() and drawOnGrid()

Using with SWColor

Bugs accept SWColor instances for consistent color management:

// Define colors using SWColor
let myRed = new SWColor(0, 100, 100, 100, "myRed");  // HSB format
let myBlue = new SWColor(240, 100, 100, 100, "myBlue");

// Create bugs with those colors
let bug1 = new SWBug(0, 0, 'r', 0.5, myRed);
let bug2 = new SWBug(5, 5, 'p', 0.3, myBlue);

Source Code

The complete SWBug class implementation:

Show/Hide Source Code
// swBug.js
// SWBug: A moving point (bug) that extends SWPoint and supports random walk or Perlin noise movement
// Author: TechNoviceTools (TNT)
// Date: 2026-01-26

console.log("[swBug.js] SWBug class loaded.");

class SWBug extends SWPoint {
    constructor(x, y, mode = 'r', speed = 1, strokeColor = undefined, options = {}) {
        super(x, y, undefined, options.strokeWeight || 5, strokeColor);
        this.mode = mode; // 'r' or 'p'
        this.speed = speed;
        this.noiseAmount = options.noiseAmount || 0.01; // Only for Perlin
        this.origin = {x: x, y: y};
        this._noiseSeedX = Math.random() * 1000;
        this._noiseSeedY = Math.random() * 1000;
        this._t = 0; // Perlin time
        this.shouldShowTrails = false;
        this.trail = [];
        this.trailPersistence = options.trailPersistence || 50;
        this.shouldWrapAround = options.shouldWrapAround || true;
    }

    move(grid) {
        if (this.shouldShowTrails) {
            this.trail.push({x: this.x, y: this.y, break: false});
            if (this.trail.length > this.trailPersistence) {
                this.trail.shift();
            }
        } else {
            this.trail = [];
        }
        
        if (this.mode === 'r') {
            // Random walk
            const angle = Math.random() * 2 * Math.PI;
            this.x += this.speed * Math.cos(angle);
            this.y += this.speed * Math.sin(angle);
        } else if (this.mode === 'p') {
            // Perlin noise
            this._t += this.noiseAmount;
            const nx = noise(this._noiseSeedX + this._t);
            const ny = noise(this._noiseSeedY + this._t);
            const dx = (nx - 0.5) * 2 * this.speed;
            const dy = (ny - 0.5) * 2 * this.speed;
            this.x += dx;
            this.y += dy;
        }
        
        // Wrap around
        if (this.shouldWrapAround && grid) {
            let wrapped = false;
            if (this.x < grid.UL.x) { this.x = grid.LR.x; wrapped = true; }
            else if (this.x > grid.LR.x) { this.x = grid.UL.x; wrapped = true; }
            if (this.y < grid.LR.y) { this.y = grid.UL.y; wrapped = true; }
            else if (this.y > grid.UL.y) { this.y = grid.LR.y; wrapped = true; }
            
            if (wrapped && this.shouldShowTrails) {
                this.trail.push({x: this.x, y: this.y, break: true});
                if (this.trail.length > this.trailPersistence) {
                    this.trail = this.trail.slice(-this.trailPersistence);
                }
            }
        }
    }

    drawOnGrid(grid) {
        // Draw trail
        if (this.shouldShowTrails && this.trail.length > 1) {
            stroke(this.strokeColor ? this.strokeColor.col : 0);
            strokeWeight(Math.max(1, this.strokeWeight / 3));
            noFill();
            let drawing = false;
            for (let i = 0; i < this.trail.length; i++) {
                const pos = this.trail[i];
                const {x, y} = grid.userToScreen(pos.x, pos.y);
                if (pos.break) {
                    if (drawing) { endShape(); drawing = false; }
                    beginShape();
                    vertex(x, y);
                    drawing = true;
                } else {
                    if (!drawing) { beginShape(); drawing = true; }
                    vertex(x, y);
                }
            }
            if (drawing) {
                const {x, y} = grid.userToScreen(this.x, this.y);
                vertex(x, y);
                endShape();
            }
        }
        // Draw bug
        super.drawOnGrid(grid);
    }

    setTrailPersistence(n) {
        this.trailPersistence = n;
        if (this.trail.length > n) {
            this.trail = this.trail.slice(-n);
        }
    }

    setShouldWrapAround(shouldWrap) {
        this.shouldWrapAround = shouldWrap;
    }

    get distanceFromOriginal() {
        return Math.sqrt((this.x - this.origin.x) ** 2 + (this.y - this.origin.y) ** 2);
    }

    toString() {
        return `SWBug(x: ${this.x.toFixed(2)}, y: ${this.y.toFixed(2)}, mode: ${this.mode}, speed: ${this.speed}, distFromOrigin: ${this.distanceFromOriginal.toFixed(2)})`;
    }

    setSpeed(newSpeed) { this.speed = newSpeed; }
    setMode(newMode) { this.mode = newMode; }
    toggleMode() { this.mode = (this.mode === 'r') ? 'p' : 'r'; }
    setShouldShowTrails(show) { this.shouldShowTrails = show; }
}