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:
- Chooses a random direction (0 to 360 degrees)
- Moves by its speed value in that direction
- 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:
- Samples Perlin noise functions for X and Y directions
- Uses these smoothly-changing values to determine movement direction
- 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 |
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 coordinatesstrokeWeight- Point thicknessstrokeColor- SWColor instance for colorpenOn,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 coordinatesmove(dx, dy, dz)- Move by delta values (note: different from SWBug'smove(grid))setPen(on),setMaxTrailLength(n),clearTrail()- SWPoint trail methodssetStrokeColor(swColor),setStrokeWeight(w)- Style methodsdistanceTo(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
trailPersistenceto 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
noiseAmountcarefully: 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()whenshouldWrapAroundis true - wrapping won't work - Confusing SWBug's trail system with SWPoint's pen trail - they're separate
- Setting
noiseAmounttoo 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()anddrawOnGrid()
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; }
}