SketchWave Color Reference

Understanding and Using SWColor - Stage

Back to SWColor Demo

...loading...

SWColor Class Reference

Overview

The SWColor class represents colors in HSB (Hue, Saturation, Brightness) color space with alpha transparency. It provides a consistent color management system for the SketchWave ecosystem and integrates seamlessly with p5.js.

Key Features:

  • HSB color space with intuitive color manipulation
  • Alpha transparency support (0-100)
  • Predefined Colors: 35+ color constants for easy access
  • Color manipulation methods (darken, brighten, saturate, desaturate)
  • Color creation from RGB and Hex values
  • Conversion to Hex and RGB strings
  • Static copy constructor for color duplication
  • Parameter cycling for animations
  • Defensive programming with automatic value clamping
Color Space: SWColor uses HSB values where:
  • Hue (H): 0-360 degrees (color wheel position)
  • Saturation (S): 0-100% (color intensity)
  • Brightness (B): 0-100% (lightness/darkness)
  • Alpha (A): 0-100% (transparency)

Constructor

new SWColor(h, s, b, a, name)

Parameters:

Parameter Type Range Default Description
h number 0-360 required Hue value (wraps around if out of range)
s number 0-100 required Saturation percentage
b number 0-100 required Brightness percentage
a number 0-100 100 Alpha (opacity) percentage
name string - "" Optional descriptive name for the color

Examples:

// Create a pure red color
let red = new SWColor(0, 100, 100);

// Create a named blue color with 50% transparency
let semiBlue = new SWColor(240, 100, 100, 50, "semiTransparentBlue");

// Create a desaturated yellow
let paleYellow = new SWColor(60, 41, 100, 100, "paleYellow");

// Hue wraps around automatically
let purple = new SWColor(420, 100, 80); // Same as h=60
Important: Values outside valid ranges are automatically clamped. Hue values wrap around (e.g., 370° becomes 10°, -10° becomes 350°). This defensive programming ensures colors remain valid without throwing errors.

Properties

Property Type Description
h number Hue value (0-360 degrees on color wheel)
s number Saturation percentage (0-100)
b number Brightness percentage (0-100)
a number Alpha/opacity percentage (0-100)
name string Optional descriptive name for the color
col p5.Color The underlying p5.js color object (auto-synced)
Note: The col property is automatically updated whenever h, s, b, or a values change through the class methods. Direct modification of h/s/b/a properties should be followed by manually recreating col if needed.

Static Methods

SWColor.copy(other)

Creates a new SWColor instance with the same properties as the given SWColor.

Parameters:
  • other (SWColor): The color to copy
Returns: SWColor - A new color instance
let originalColor = new SWColor(180, 50, 75, 100, "cyan");
let copiedColor = SWColor.copy(originalColor);
// copiedColor is independent of originalColor
Design Note: This is a static method (called on the class) rather than an instance method, following JavaScript best practices for copy constructors since JavaScript doesn't support multiple constructors.

SWColor.fromRGB(r, g, b, a, name)

Creates a SWColor from RGB values (0-255).

Parameters:
  • r (number): Red value (0-255)
  • g (number): Green value (0-255)
  • b (number): Blue value (0-255)
  • a (number): Alpha percentage (0-100), default: 100
  • name (string): Optional name, default: ""
Returns: SWColor - New color in HSB space
// Create orange from RGB
let orange = SWColor.fromRGB(255, 165, 0, 100, "orange");

// Semi-transparent green
let greenGlass = SWColor.fromRGB(0, 255, 0, 30, "greenGlass");

SWColor.fromHex(hex, alpha, name)

Creates a SWColor from a hexadecimal color string.

Parameters:
  • hex (string): Hex color string (with or without #)
  • alpha (number): Optional alpha (0-100), default: 100
  • name (string): Optional name, default: ""
Returns: SWColor - New color in HSB space
// Create from hex with #
let crimson = SWColor.fromHex("#DC143C", 100, "crimson");

// Create from hex without # (fully opaque)
let gold = SWColor.fromHex("FFD700", 100, "gold");

// Semi-transparent
let ghostBlue = SWColor.fromHex("#0000FF", 50, "ghostBlue");

Instance Methods

Conversion Methods

toString()

Returns a string representation of the color with all properties.

Returns: string
let myColor = new SWColor(240, 100, 100, 80, "blue");
console.log(myColor.toString());
// Output: "SWColor(name: 'blue', h: 240, s: 100, b: 100, a: 80)"
getHexStr()

Converts the color to a CSS hexadecimal string.

Returns: string - Hex color (e.g., "#FF0000")
let red = new SWColor(0, 100, 100);
console.log(red.getHexStr()); // "#FF0000"
getRGBStr()

Returns RGB values as a formatted string.

Returns: string - RGB string (e.g., "(255, 0, 0)")
let green = new SWColor(120, 100, 100);
console.log(green.getRGBStr()); // "(0, 255, 0)"

Color Manipulation Methods (Mutating)

These methods modify the color in place and return the color for chaining.

darkenBy(factor)

Decreases brightness by multiplying by (1 - factor).

Parameters:
  • factor (number): Amount to darken (e.g., 0.2 = 20% darker)
let myColor = new SWColor(120, 100, 100);
myColor.darkenBy(0.3); // 30% darker
brightenBy(factor)

Increases brightness by multiplying by (1 + factor).

Parameters:
  • factor (number): Amount to brighten (e.g., 0.2 = 20% brighter)
let myColor = new SWColor(240, 100, 50);
myColor.brightenBy(0.5); // 50% brighter
saturateBy(factor)

Increases saturation by multiplying by (1 + factor).

Parameters:
  • factor (number): Amount to saturate (e.g., 0.2 = 20% more saturated)
Returns: this (for chaining)
let myColor = new SWColor(0, 50, 100);
myColor.saturateBy(0.4); // 40% more saturated
desaturateBy(factor)

Decreases saturation by multiplying by (1 - factor).

Parameters:
  • factor (number): Amount to desaturate (e.g., 0.3 = 30% less saturated)
Returns: this (for chaining)
let myColor = new SWColor(180, 80, 100);
myColor.desaturateBy(0.5); // 50% less saturated
changeAlphaBy(factor)

Changes alpha by multiplying by (1 + factor). Factor can be positive or negative.

Parameters:
  • factor (number): Amount to change alpha (e.g., 0.2 = 20% more opaque, -0.5 = 50% more transparent)
Returns: this (for chaining)
let myColor = new SWColor(0, 100, 100, 100);
myColor.changeAlphaBy(-0.5); // 50% more transparent
setAlphaTo(a)

Sets alpha to a specific value.

Parameters:
  • a (number): New alpha value (0-100)
Returns: this (for chaining)
let myColor = new SWColor(120, 100, 100);
myColor.setAlphaTo(50); // Set to 50% transparent

Color Creation Methods (Non-Mutating)

These methods return new SWColor instances without modifying the original.

createDarkerColor(factor)

Returns a new SWColor with brightness scaled by factor (0-1 = darker, >1 = brighter).

Parameters:
  • factor (number): Brightness multiplier
Returns: SWColor - New darker color
let baseColor = new SWColor(240, 100, 100, 100, "blue");
let darkerBlue = baseColor.createDarkerColor(0.5); // 50% of brightness
// baseColor is unchanged
createLighterColor(factor)

Returns a new SWColor with brightness increased by factor.

Parameters:
  • factor (number): Brightness multiplier
Returns: SWColor - New lighter color
let baseColor = new SWColor(0, 100, 60, 100, "red");
let lighterRed = baseColor.createLighterColor(1.3); // 130% of brightness
// baseColor is unchanged
createAdjustedSaturation(factor)

Returns a new SWColor with saturation scaled by factor.

Parameters:
  • factor (number): Saturation multiplier
Returns: SWColor - New color with adjusted saturation
let vibrant = new SWColor(120, 100, 100, 100, "green");
let muted = vibrant.createAdjustedSaturation(0.3); // 30% saturation
// vibrant is unchanged

Animation Methods

cycleParameter(param, sinusoid)

Cycles a color parameter using a SWSinusoid object for animations.

Parameters:
  • param (string): "h", "s", "b", or "a"
  • sinusoid (SWSinusoid): Sinusoid object with getValue() method
// Requires SWSinusoid class
let hueSin = new SWSinusoid(0, 360, 0.01);
let myColor = new SWColor(0, 100, 100);

function draw() {
    hueSin.update();
    myColor.cycleParameter("h", hueSin);
    // Color hue now cycles through rainbow
}

Utility Methods

verify(prop, min, max)

Internal method that clamps a property to the specified range. Automatically called by the constructor and manipulation methods.

Parameters:
  • prop (string): Property name ("h", "s", "b", or "a")
  • min (number): Minimum allowed value
  • max (number): Maximum allowed value

Predefined Colors

SWColor includes 35+ predefined color constants. Call initializeSWColors() in your setup() function after setting colorMode(HSB, 360, 100, 100, 100).

Initialization:

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
    initializeSWColors(); // Must be called after colorMode
}

Available Colors:

Grayscale
  • swBlack
  • swWhite
  • swOffWhite
  • swLightGray
  • swDarkGray
Reds & Browns
  • swRed
  • swMaroon
  • swBrown
  • swLightBrown
  • swTrunkBrown
  • swDarkFlesh
  • swLightFlesh
Oranges & Yellows
  • swOrange
  • swGold
  • swYellow
  • swDarkYellow
  • swDeepYellow
  • swPaleYellow
Greens
  • swGreen
  • swMedGreen
  • swDarkGreen
  • swForestGreen
  • swCyan
Blues
  • swBlue
  • swLightBlue
  • swLightishBlue
  • swDarkBlue
  • swDeepBlue
  • swNavy
Purples & Pinks
  • swPurple
  • swDarkPurple
  • swIndigo
  • swViolet
  • swMagenta
  • swPink
  • swHotPink
  • swDeepPink

Usage Example:

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
    initializeSWColors();
}

function draw() {
    background(swOffWhite.col);
    fill(swRed.col);
    circle(200, 200, 100);
}

Usage Examples

Example 1: Basic Color Creation and Use

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
}

function draw() {
    background(220);
    
    // Create and use custom colors
    let skyBlue = new SWColor(200, 60, 100);
    let grass = new SWColor(120, 80, 60);
    
    fill(skyBlue.col);
    rect(0, 0, 400, 200);
    
    fill(grass.col);
    rect(0, 200, 400, 200);
}

Example 2: Color Manipulation

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
}

function draw() {
    background(220);
    
    let baseColor = new SWColor(180, 100, 80, 100, "cyan");
    
    // Original color
    fill(baseColor.col);
    circle(100, 200, 80);
    
    // Darker version (non-mutating)
    let dark = baseColor.createDarkerColor(0.5);
    fill(dark.col);
    circle(200, 200, 80);
    
    // Desaturated version
    let muted = SWColor.copy(baseColor);
    muted.desaturateBy(0.7);
    fill(muted.col);
    circle(300, 200, 80);
}

Example 3: Creating Colors from RGB and Hex

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
    initializeSWColors();
}

function draw() {
    background(swWhite.col);
    
    // From RGB
    let coral = SWColor.fromRGB(255, 127, 80, 100, "coral");
    fill(coral.col);
    circle(150, 200, 100);
    
    // From Hex
    let lavender = SWColor.fromHex("#E6E6FA", "lavender");
    fill(lavender.col);
    circle(250, 200, 100);
    
    // Log color info
    console.log(coral.toString());
    console.log("Hex:", coral.getHexStr());
    console.log("RGB:", coral.getRGBStr());
}

Example 4: Animated Gradient

let topColor, bottomColor;

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
    topColor = new SWColor(200, 100, 100);
    bottomColor = new SWColor(280, 100, 100);
}

function draw() {
    // Animate colors
    topColor.h = (topColor.h + 0.5) % 360;
    bottomColor.h = (bottomColor.h + 0.3) % 360;
    
    // Draw gradient
    for (let y = 0; y < height; y++) {
        let inter = map(y, 0, height, 0, 1);
        let h = lerp(topColor.h, bottomColor.h, inter);
        let s = lerp(topColor.s, bottomColor.s, inter);
        let b = lerp(topColor.b, bottomColor.b, inter);
        
        let lineColor = new SWColor(h, s, b);
        stroke(lineColor.col);
        line(0, y, width, y);
    }
}

Example 5: Method Chaining

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
}

function draw() {
    background(220);
    
    let myColor = new SWColor(300, 80, 90, 100, "magenta");
    
    // Chain multiple operations
    myColor.saturateBy(0.2)
           .darkenBy(0.1)
           .setAlphaTo(70);
    
    fill(myColor.col);
    circle(200, 200, 150);
}

Example 6: Using Predefined Colors

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
    initializeSWColors(); // Initialize predefined colors
}

function draw() {
    background(swOffWhite.col);
    
    // Sun
    fill(swYellow.col);
    circle(100, 100, 60);
    
    // Tree trunk
    fill(swTrunkBrown.col);
    rect(180, 250, 40, 100);
    
    // Tree leaves
    fill(swForestGreen.col);
    circle(200, 230, 100);
    
    // Sky accent
    fill(swLightishBlue.col);
    noStroke();
    circle(320, 80, 40);
}

Integration with SketchWave Ecosystem

Dependencies

  • p5.js: Required for color creation and manipulation
  • SWSinusoid: Optional, for parameter cycling animations

Loading Order

Ensure scripts are loaded in this order in your HTML:

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

<!-- SketchWave classes -->
<script src="scripts/swColor.js"></script>
<script src="scripts/swPoint.js"></script>
<script src="scripts/swSinusoid.js"></script>
<!-- Your sketch script -->
<script src="scripts/mySketch.js"></script>

Setup Requirements

Critical: Always set colorMode(HSB, 360, 100, 100, 100) in your setup() function before using SWColor. Call initializeSWColors() after setting the color mode if you want to use predefined colors.
function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100); // REQUIRED
    initializeSWColors(); // Optional, for predefined colors
}

Best Practices

  • Always use colorMode(HSB, 360, 100, 100, 100) for consistency
  • Use .col property when passing colors to p5.js functions (fill, stroke, etc.)
  • Prefer non-mutating methods (createDarkerColor) when you need to preserve the original
  • Use mutating methods (darkenBy) for in-place modifications and chaining
  • Name your colors for easier debugging and code readability
  • Use SWColor.copy() when you need an independent duplicate
  • Leverage predefined colors for common use cases to ensure consistency

Common Patterns

// Pattern 1: Create variations of a base color
let base = new SWColor(180, 80, 80, 100, "baseColor");
let light = base.createLighterColor(1.3);
let dark = base.createDarkerColor(0.6);
let muted = base.createAdjustedSaturation(0.4);

// Pattern 2: Modify a color over time
let dynamicColor = new SWColor(0, 100, 100);
function draw() {
    dynamicColor.h = (frameCount * 2) % 360;
    dynamicColor.col = color(dynamicColor.h, dynamicColor.s, dynamicColor.b, dynamicColor.a);
    fill(dynamicColor.col);
}

// Pattern 3: Use with other SketchWave classes
let myPoint = new SWPoint(100, 100, undefined, 5, swRed);
myPoint.draw();

Complete Source Code

View the complete, documented source code for the SWColor class and initialization function:

Show/Hide Source Code
/*
File: swColor.js
Date: 2026-02-20
Author: klp
Workspace: SketchWaveTNT2026-02-19-Stg4
Purpose: SWColor class and predefined colors for SketchWaveJS
Comment(s):

TODO:

=== Notes ===:
- SWColor class represents colors in HSB color space with alpha transparency.
- Includes methods for color manipulation (darken, brighten, saturate, 
    desaturate, change alpha).
- Predefined global SWColor instances for common colors (swRed, swBlue, swGreen, etc.).
- Initialization function to set up all predefined colors.
- Static method to create SWColor from RGB values.
- Static copy method to duplicate SWColor instances.
- Verification of color property ranges to ensure valid values.
- Script must be loaded before the p5js script, but colors are initialized in setup() after    colorMode is set.
- Code is only compatible with p5.js environment.
- Additional methods: createDarkerColor(factor), createLighterColor(factor) and createAdjustedSaturation(factor)

*/

console.log("[swColor.js] SWColor class and predefined colors loaded");

// Declare all SWColor variables (uninitialized)
var swBlack, swWhite, swOffWhite, swLightGray, swDarkGray;
var swRed, swDarkFlesh, swOrange, swLightFlesh, swBrown;
var swLightBrown, swTrunkBrown, swGold, swDarkYellow, swYellow, swDeepYellow, swPaleYellow;
var swMedGreen, swDarkGreen, swForestGreen, swGreen, swCyan;
var swLightishBlue, swDarkBlue, swDeepBlue, swBlue, swLightBlue;
var swNavy, swIndigo, swViolet, swDarkPurple, swMagenta, swPurple;
var swPink, swHotPink, swDeepPink, swMaroon;

function initializeSWColors() {
    console.log("...initializeSWColors..."); 
    //must be called AFTER colorMode is set in setup()
    // This function initializes all global SWColor instances
    
    // In order of ascending hue
    swBlack = new SWColor(0, 0, 0, 100, "black");
    swWhite = new SWColor(0, 0, 100, 100, "white");
    swOffWhite = new SWColor(0, 0, 93, 100, "offWhite");
    swLightGray = new SWColor(0, 0, 80, 100, "lightGray");
    swDarkGray = new SWColor(0, 0, 40, 100, "darkGray");
    swRed = new SWColor(0, 100, 100, 100, "red");
    swDarkFlesh = new SWColor(15, 46, 61, 100, "darkFlesh");
    swOrange = new SWColor(18, 80, 100, 100, "orange");
    swLightFlesh = new SWColor(18, 24, 74, 100, "lightFlesh");
    swBrown = new SWColor(27, 100, 65, 100, "brown");
    swLightBrown = new SWColor(30, 100, 80, 100, "lightBrown");
    swTrunkBrown = new SWColor(40, 86, 54, 100, "trunkBrown");
    swGold = new SWColor(45, 94, 92, 100, "gold");
    swDarkYellow = new SWColor(59, 100, 80, 100, "darkYellow");
    swYellow = new SWColor(60, 100, 100, 100, "yellow");
    swDeepYellow = new SWColor(60, 100, 59, 100, "deepYellow");
    swPaleYellow = new SWColor(60, 41, 100, 100, "paleYellow");
    swMedGreen = new SWColor(110, 36, 52, 100, "medGreen");
    swDarkGreen = new SWColor(133, 87, 40, 100, "darkGreen");
    swForestGreen = new SWColor(146, 100, 46, 100, "forestGreen");
    swGreen = new SWColor(120, 100, 100, 100, "green");
    swCyan = new SWColor(180, 100, 100, 100, "cyan");
    swLightishBlue = new SWColor(200, 60, 100, 100, "lightishBlue");
    swDarkBlue = new SWColor(204, 100, 100, 100, "darkBlue");
    swDeepBlue = new SWColor(228, 100, 100, 100, "deepBlue");
    swBlue = new SWColor(240, 100, 100, 100, "blue");
    swLightBlue = new SWColor(240, 22, 100, 100, "lightBlue");
    swNavy = new SWColor(240, 100, 50, 100, "navy");
    swIndigo = new SWColor(255, 100, 40, 100, "indigo");
    swViolet = new SWColor(282, 100, 27, 100, "violet");
    swDarkPurple = new SWColor(270, 100, 80, 100, "darkPurple");
    swMagenta = new SWColor(300, 100, 100, 100, "magenta");
    swPurple = new SWColor(300, 67, 60, 100, "purple");
    swPink = new SWColor(320, 25, 100, 100, "pink");
    swHotPink = new SWColor(324, 100, 100, 100, "hotPink");
    swDeepPink = new SWColor(334, 100, 79, 100, "deepPink");
    swMaroon = new SWColor(348, 100, 71, 100, "maroon");
}//end initializeSWColors

class SWColor {  
    constructor(h, s, b, a = 100, name = "") {
        //hue: 0-360, saturation: 0-100, brightness: 0-100, alpha: 0-100
        //wraparound hue to be within 0-360
        let anH = h;
        if(h < 0){
            anH = 360 + (h % 360);
        }else{
            anH = h % 360;  
        }
        if(anH !== h) console.log(`SWColor hue adjusted to ${anH}`);
        this.h = anH;
        this.s = s;
        this.b = b;
        this.a = a;
        this.name = name;

        //defensive programming: verify values are in range
        //hue is verified with wraparound above
        this.verify('s', 0, 100);
        this.verify('b', 0, 100);
        this.verify('a', 0, 100);

        // Create and store the p5.js color object
        //console.log(`SWColor created: ${this.toString()}`);
        this.col = color(this.h, this.s, this.b, this.a);
    }//end constructor

    // Copy constructor: returns a new SWColor with the same 
    // properties as the given SWColor instance
    static copy(other) {
        if (!(other instanceof SWColor)) {
            throw new Error('Argument to SWColor.copy must be an SWColor instance');
        }
        return new SWColor(other.h, other.s, other.b, other.a, other.name);
    }//end copy

    toString() {
        return `SWColor(name: '${this.name}', h: ${this.h}, s: ${this.s}, b: ${this.b}, a: ${this.a})`;
    }//end toString

    getHexStr() {
        // Converts this.col (p5.Color) to a CSS hex string
        // p5.js: red(), green(), blue() extract RGB from color object
        let r = Math.round(red(this.col));
        let g = Math.round(green(this.col));
        let b = Math.round(blue(this.col));
        return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
    }//end getHexStr

    getRGBStr() {
        // Returns a string in the format (r, g, b)
        // DWR! red, green, blue are p5.js functions!
        let r = red(this.col);
        let g = green(this.col);
        let b = blue(this.col);
        return `(${r}, ${g}, ${b})`;
    }//end getRGBStr

    verify(prop, min, max) {
        //ensures that the given property (h, s, b, a) is within the specified range
        if (this[prop] < min) {
            this[prop] = min;
        } else if (this[prop] > max) {
            this[prop] = max;
        }
    }//end verify

    darkenBy(factor) {
        // Decrease brightness by multiplying by (1 - factor), 
        // e.g., factor=0.2 darkens by 20%
        const originalB = this.b;
        this.b = originalB * (1 - factor);
        this.verify('b', 0, 100);
        this.col = color(this.h, this.s, this.b, this.a);
    }//end darkenBy

    brightenBy(factor) {
        // Increase brightness by multiplying by (1 + factor), 
        // e.g., factor=0.2 brightens by 20%
        const originalB = this.b;
        this.b = originalB * (1 + factor);
        this.verify('b', 0, 100);
        this.col = color(this.h, this.s, this.b, this.a);
    }//end brightenBy

    saturateBy(factor) {
        // Increase saturation by multiplying by (1 + factor), 
        // e.g., factor=0.2 increases saturation by 20%
        const originalS = this.s;
        this.s = originalS * (1 + factor);
        this.verify('s', 0, 100);
        this.col = color(this.h, this.s, this.b, this.a);
        return this;
    }//end saturateBy

    desaturateBy(factor) {
        // Decrease saturation by multiplying by (1 - factor), 
        // e.g., factor=0.2 decreases saturation by 20%
        const originalS = this.s;
        this.s = originalS * (1 - factor);
        this.verify('s', 0, 100);
        this.col = color(this.h, this.s, this.b, this.a);
        return this;
    }//end desaturateBy

    changeAlphaBy(factor) {
        // factor can be positive or negative
        // Change alpha by multiplying by (1 + factor), 
        // e.g., factor=0.2 increases alpha by 20%  
        const originalA = this.a;
        this.a = originalA * (1 + factor);
        this.verify('a', 0, 100);
        this.col = color(this.h, this.s, this.b, this.a);
        return this;
    }//end changeAlphaBy

    setAlphaTo(a) {
        this.a = a;
        this.verify('a', 0, 100);
        this.col = color(this.h, this.s, this.b, this.a);
        return this;
    }//end setAlphaTo

    // Static method: create SWColor from RGB values
    static fromRGB(r, g, b, a = 100, name = "") {
        // Convert RGB (0-255) to HSB (h: 0-360, s: 0-100, b: 0-100)
        r /= 255; g /= 255; b /= 255;
        let max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, v = max;
        let d = max - min;
        s = max === 0 ? 0 : d / max;
        if (max === min) {
            h = 0;
        } else {
            switch (max) {
                case r: h = ((g - b) / d) % 6; break;
                case g: h = ((b - r) / d) + 2; break;
                case b: h = ((r - g) / d) + 4; break;
            }
            h *= 60;
            if (h < 0) h += 360;
        }
        return new SWColor(Math.round(h), Math.round(s * 100), Math.round(v * 100), a, name);
    }//end fromRGB

    static fromHex(hex, alpha = 100, name = "") {
        // Ensure hex starts with '#'
        if (!hex.startsWith("#")) hex = "#" + hex;
        // Use p5.js color() to parse hex
        const c = color(hex);
        // Extract HSB values
        colorMode(HSB, 360, 100, 100, 100);
        const h = hue(c);
        const s = saturation(c);
        const br = brightness(c);
        return new SWColor(h, s, br, alpha, name);
    }//end fromHex

    cycleParameter(param, sinusoid) {
        // param: "h", "s", "b", or "a"
        // sinusoid: SWSinusoid object with a getValue() method
        if (!["h", "s", "b", "a"].includes(param)) {
            throw new Error("Invalid parameter for SWColor.cycleParameter");
        }
        this[param] = sinusoid.getValue();
        if (param === "h") {
            //wraparound hue to be within 0-360
            if(this.h < 0){
                this.h = 360 + (this.h % 360);
            }else{
                this.h = this.h % 360;  
            }
        }else{
            this.verify(param, 0, 100);
        }
        this.col = color(this.h, this.s, this.b, this.a);
    }//end cycleParameter

    /**
     * Returns a new SWColor with brightness scaled by the 
     * given factor (0-1 = darker, >1 = brighter)
     * @param {number} factor - The factor to scale brightness (b)
     * @returns {SWColor} New SWColor with adjusted brightness
     */
    createDarkerColor(factor) {
        // Clamp factor to non-negative
        factor = Math.max(0, factor);
        let newB = this.b * factor;
        // Clamp brightness to [0, 100]
        newB = Math.max(0, Math.min(100, newB));
        return new SWColor(this.h, this.s, newB, this.a, this.name ? this.name + '_darker' : undefined);
    }//end createDarkerColor

    /**
     * Returns a new SWColor with brightness increased by the given factor (>1 = lighter, 0-1 = dimmer)
     * @param {number} factor - The factor to scale brightness (b)
     * @returns {SWColor} New SWColor with adjusted brightness
     */
    createLighterColor(factor) {
        factor = Math.max(0, factor);
        let newB = this.b * factor;
        newB = Math.max(0, Math.min(100, newB));
        return new SWColor(this.h, this.s, newB, this.a, this.name ? this.name + '_lighter' : undefined);
    }//end createLighterColor

    /**
     * Returns a new SWColor with saturation scaled by the given factor
     * @param {number} factor - The factor to scale saturation (s)
     * @returns {SWColor} New SWColor with adjusted saturation
     */
    createAdjustedSaturation(factor) {
        factor = Math.max(0, factor);
        let newS = this.s * factor;
        newS = Math.max(0, Math.min(100, newS));
        return new SWColor(this.h, newS, this.b, this.a, this.name ? this.name + '_sat' : undefined);
    }//end createAdjustedSaturation


    /* 
    --- Notes ---
    'verify' method ensures color properties stay within valid ranges. It used to throw an error using code like this:
    if (this[prop] < min || this[prop] > max) {
        throw new Error(`${prop} value ${this[prop]} is out of range (${min}-${max})`);
    }   
    However, this could disrupt program flow if a color value was slightly out of bounds due to calculations.
    Instead, it now clamps the value to the nearest valid limit and logs a message. This makes the class more robust

    Regarding static copy method:
    A static copy method is used instead of a traditional 
    copy constructor because JavaScript does not support multiple constructors.
    Using a static method allows us to create a new instance based on an existing one without overloading the constructor.
    This approach is clear and idiomatic in JavaScript, providing a straightforward way to duplicate objects.

    The static keyword before copy means that copy is a static method of the SWColor class. This means you call it on the class itself (SWColor.copy(...)), not on an instance (not swRed.copy(...)).

    It is necessary if you want to use the method without needing an instance, and it makes sense here: copy creates a new SWColor from an existing one, but doesn't depend on the state of a particular SWColor instance.

    If you removed static, you would have to call copy on an instance (e.g., swRed.copy(other)), which is less clear for a copy constructor pattern. So, static is appropriate and recommended in this case.
    */

}//end SWColor class
Developer Notes: This reference guide is part of the SketchWave ecosystem. SWColor is the foundation for color management across all SketchWave classes. For questions or contributions, please refer to the SketchWave documentation or contact the development team.