SWWheel is a composite graphic class that renders an animated
wagon-wheel on a p5.js canvas. It is built entirely from existing
SketchWaveJS components — no raw p5 drawing calls appear in
SWWheel itself. The class supports continuous rotation,
sinusoidal radius oscillation (“breathing”), and full
independent color control for its three visual parts.
SWWheel
├── hub → SWDisk (filled center disk — drawn on top)
├── rim → SWDisk (outlined ring at outer radius, stroke only)
└── spokes → SWLine[] (n evenly-spaced lines, center → rim edge)
Draw order: rim → spokes → hub
Coordinate system:
The center point and all radii are in user/grid units.
Stroke weights (rimThickness, spokeThickness) are
always in screen pixels. Use drawOnGrid(grid) for the
normal canvas workflow; use draw() only when working directly
in raw pixel coordinates.
Dependencies
All five must be loaded in your HTML beforeswWheel.js:
Class / Library
File
Role in SWWheel
p5.js
CDN
Canvas, rendering engine — strokeWeight(), line(), etc.
SWColor
shapeClasses/swColor.js
HSB color objects for hub fill, rim stroke, and spoke stroke
SWPoint
shapeClasses/swPoint.js
Center point and computed spoke endpoints
SWLine
shapeClasses/swLine.js
Each individual spoke
SWDisk
shapeClasses/swDisk.js
Hub (filled) and rim (outlined)
SWGrid
shapeClasses/swGrid.js
Required only for drawOnGrid()
Constructor
const wheel = new SWWheel({ /* options object — all optional */ });
All options are passed as a single destructured object. Every key has a
sensible default, so new SWWheel() with no arguments is valid
and produces an 8-spoke wheel centered at the grid origin.
Option
Type
Default
Description
center
SWPoint
SWPoint(0, 0)
Wheel center in user/grid coordinates
outerRadius
number
80
Rim radius in user units. Also seeds originalOuterRadius.
hubRadius
number
10
Hub disk radius in user units
numSpokes
number
8
Number of evenly-distributed spokes
rimThickness
number
3
Rim stroke weight in pixels. 0 = invisible rim.
spokeThickness
number
2
Spoke stroke weight in pixels
hubFillColor
SWColor
undefined
Hub fill. undefined lets p5 use its current fill state.
rimColor
SWColor
undefined
Rim stroke and hub border color (shared)
spokeColor
SWColor
undefined
Spoke stroke color. Each spoke receives its own defensive copy.
Defensive copying: All three color options (and their
corresponding setters) apply copy-on-intake. Mutating a
SWColor object after passing it to the constructor will not
silently change the wheel’s appearance.
See the chatlog Turn 10
for a full explanation of the aliasing footgun this prevents.
Public Properties
All constructor options become readable instance properties.
For most of them, prefer the setter methods
over direct assignment — setters keep the internal
SWDisk and SWLine components synchronized.
Property
Type
Notes
center
SWPoint
Wheel center (user/grid coordinates)
outerRadius
number
Nominal rim radius; updated by setOuterRadius()
originalOuterRadius
number
Baseline radius; reset() restores currentRadius to this value
hubRadius
number
Hub disk radius (user units)
numSpokes
number
Current spoke count
rimThickness
number
Rim stroke weight in pixels (0 = hidden)
spokeThickness
number
Spoke stroke weight in pixels
hubFillColor
SWColor
Owned copy of the hub fill color
rimColor
SWColor
Owned copy of the rim stroke + hub border color
spokeColor
SWColor
Owned copy of the spoke color template
rotationAngle
number
Current accumulated rotation in degrees; set by rotate()
currentRadius
number
Live rim radius. Equals outerRadius when breathe() is not active.
hub
SWDisk
Hub component (read-only intent)
rim
SWDisk
Rim component (read-only intent)
spokes
SWLine[]
Spoke array (read-only intent)
Methods
Drawing
Signature
Returns
Notes
draw()
void
Draws in raw screen/pixel coordinates. Use only when not working with a grid.
drawOnGrid(grid)
void
Preferred. Draws in user/grid coordinates via
the given SWGrid. Draw order:
rim → spokes → hub.
Animation
Signature
Parameters
Notes
rotate(degPerSec, t)
degPerSec: number t: number (s)
Sets rotationAngle = degPerSec × t and
repositions all spokes. Positive = CCW in user space (CW on
screen, due to Y-axis inversion). Call every frame.
breathe(sinusoid, t)
sinusoid: SWSinusoid t: number (s)
Sets currentRadius from
sinusoid.getValue(t), updates the rim disk,
and repositions spokes. Call every frame.
reset()
—
Restores rotationAngle to 0 and
currentRadius to originalOuterRadius.
Call when the user resets the sketch.
Setters
Every setter keeps the internal SWDisk and SWLine
components in sync automatically. All color setters apply
copy-on-intake.
Method
Parameter
Notes
setNumSpokes(n)
n: number
Clamped to ≥ 1. Rebuilds the entire spokes array.
setOuterRadius(r)
r: number
Updates outerRadius, originalOuterRadius, currentRadius, rim disk radius, and spoke positions.
setHubRadius(r)
r: number
Updates the hub SWDisk radius.
setSpokeThickness(w)
w: number (px)
Calls spoke.setThickness(w) on every SWLine in the array.
setRimThickness(w)
w: number (px)
Pass 0 to hide the rim entirely (guarded in draw/drawOnGrid).
setSpokeColor(swColor)
swColor: SWColor
Each spoke receives its own copy. Pass undefined to clear color.
setRimColor(swColor)
swColor: SWColor
Sets rim stroke and hub border color together (shared visual role).
Constructor only. Creates hub, rim, and initial spokes.
_buildSpokes()
Constructor and setNumSpokes(). Rebuilds the full spokes array.
_repositionSpokes()
Every call to rotate(), breathe(), reset(), and radius/count setters. Updates ptA/ptB of each existing spoke — faster than a full rebuild.
Usage Examples
Minimal wheel at the grid origin
// All defaults — 8 spokes, outerRadius 80, no colors setlet wheel, grid;
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
grid = new SWGrid( /* your grid params */ );
wheel = new SWWheel();
}
function draw() {
background(0, 0, 90);
wheel.drawOnGrid(grid);
}
Custom wheel with color and rotation
let wheel, grid, tElapsed = 0;
function setup() {
createCanvas(500, 500);
colorMode(HSB, 360, 100, 100, 100);
grid = new SWGrid( /* your grid params */ );
wheel = new SWWheel({
outerRadius: 120,
numSpokes: 10,
rimThickness: 8,
spokeThickness: 8,
rimColor: new SWColor(210, 80, 100), // blue
spokeColor: new SWColor(210, 80, 100),
hubFillColor: new SWColor( 45, 90, 92), // gold
});
}
function draw() {
tElapsed += min(deltaTime / 1000, 0.1); // seconds, capped at 100 ms
background(0, 0, 90);
wheel.rotate(45, tElapsed); // 45 °/sec CCW (user space)
wheel.drawOnGrid(grid);
}
Breathing wheel (oscillating radius)
let wave, tElapsed = 0;
function setup() {
// ...
wave = new SWSinusoid({
midValue: 100, // center radius in user units
amplitude: 30, // ± 30 units
period: 3, // 3-second cycle
});
}
function draw() {
tElapsed += min(deltaTime / 1000, 0.1);
wheel.rotate(60, tElapsed);
wheel.breathe(wave, tElapsed); // rim and spoke tips pulse together
wheel.drawOnGrid(grid);
}
Invisible rim — the plus-sign configuration
// rimThickness:0 hides the rim; hubRadius:0 hides the hub.
// Result is a clean ✚ shape — the basis for SWCross (Stage 2).const plus = new SWWheel({
numSpokes: 4,
rimThickness: 0,
hubRadius: 0,
spokeThickness: 2,
spokeColor: new SWColor(270, 40, 75),
});
Wiring UI sliders to setters
// Call once in setup() after the wheel is created.function wireWheelControls() {
document.getElementById('numSpokesSlider')
.addEventListener('input', e => wheel.setNumSpokes(+e.target.value));
document.getElementById('rimThickSlider')
.addEventListener('input', e => wheel.setRimThickness(+e.target.value));
document.getElementById('rimColorPicker')
.addEventListener('input', e => {
wheel.setRimColor(hexToSWColor(e.target.value)); // your hex helper
});
}
Source — shapeClasses/swWheel.js
Loaded live from the file so this view always reflects the
current version. Use the button to copy the full source to the clipboard.