AI Constellation Drawer - Create Your Own Star Patterns - xelsed.ai

This sketch creates an interactive constellation drawing tool where users click on twinkling stars to connect them with lines. Stars twinkle smoothly using sine wave oscillation, glow when hovered or selected, and follow a parallax effect based on mouse position. Users can clear all connections and start over anytime.

๐ŸŽ“ Concepts You'll Learn

Classes and ObjectsAnimation with sine wavesEvent handling (mouse/touch)Parallax effectCanvas gradientsCollision detectionArray managementDrawing context (WebGL)Responsive canvas

๐Ÿ”„ Code Flow

Code flow showing setup, draw, star, generatestars, clearconnections, mouseclicked, touchstarted, drawbackgroundgradient, windowresized

๐Ÿ’ก Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> canvas-creation[Canvas Setup] setup --> gradient-colors[Gradient Color Array] setup --> ui-elements[UI Button and Info Display] setup --> generatestars[generateStars] setup --> draw[draw loop] click setup href "#fn-setup" click canvas-creation href "#sub-canvas-creation" click gradient-colors href "#sub-gradient-colors" click ui-elements href "#sub-ui-elements" click generatestars href "#fn-generatestars" draw --> star-loop[Star Update and Display Loop] draw --> parallax-calculation[Parallax Effect Calculation] draw --> connection-loop[Connection Lines Drawing Loop] draw --> info-update[Info Display Update] draw --> clearconnections[clearConnections] click draw href "#fn-draw" click star-loop href "#sub-star-loop" click parallax-calculation href "#sub-parallax-calculation" click connection-loop href "#sub-connection-loop" click info-update href "#sub-info-update" click clearconnections href "#fn-clearconnections" star-loop --> constructor[Star Constructor] star-loop --> twinkling-calculation[Twinkling Alpha Calculation] star-loop --> glow-effect[Glow Effect Conditional] click constructor href "#sub-constructor" click twinkling-calculation href "#sub-twinkling-calculation" click glow-effect href "#sub-glow-effect" connection-loop --> button-check[Button Click Detection] connection-loop --> star-search[Find Clicked Star] connection-loop --> connection-logic[Connection State Machine] click button-check href "#sub-button-check" click star-search href "#sub-star-search" click connection-logic href "#sub-connection-logic" clearconnections --> clear-arrays[Clear Data Arrays] clearconnections --> deselect-loop[Deselect All Stars Loop] click clear-arrays href "#sub-clear-arrays" click deselect-loop href "#sub-deselect-loop" windowresized[windowResized] --> canvas-resize[Canvas Resize] windowresized --> star-regeneration[Star Regeneration] click windowresized href "#fn-windowresized" click canvas-resize href "#sub-canvas-resize" click star-regeneration href "#sub-star-regeneration" draw --> drawbackgroundgradient[drawBackgroundGradient] click drawbackgroundgradient href "#fn-drawbackgroundgradient" drawbackgroundgradient --> gradient-loop[Gradient Segment Loop] drawbackgroundgradient --> gradient-creation[Linear Gradient Object Creation] drawbackgroundgradient --> color-stops[Gradient Color Stops] click gradient-loop href "#sub-gradient-loop" click gradient-creation href "#sub-gradient-creation" click color-stops href "#sub-color-stops"

๐Ÿ“ Code Breakdown

setup()

setup() runs once when the sketch starts. It initializes the canvas, defines colors, generates stars, and creates UI elements. This is where you set up everything the sketch needs before animation begins.

function setup() {
  createCanvas(windowWidth, windowHeight);
  noStroke();
  colorMode(RGB);

  gradientColors = [
    color(0, 0, 20),
    color(0, 0, 40),
    color(0, 0, 60),
    color(0, 0, 80)
  ];

  generateStars();

  clearButton = createButton('Clear Constellations');
  clearButton.position(20, 20);
  clearButton.mousePressed(clearConnections);
  clearButton.addClass('p5-button');

  infoDiv = createDiv(`Stars: ${starCount} | Lines: ${lineCount}`);
  infoDiv.position(20, 60);
  infoDiv.addClass('p5-info');
}

๐Ÿ”ง Subcomponents:

initialization Canvas Setup createCanvas(windowWidth, windowHeight);

Creates a canvas that fills the entire browser window

initialization Gradient Color Array gradientColors = [color(0, 0, 20), color(0, 0, 40), color(0, 0, 60), color(0, 0, 80)];

Defines four shades of dark blue for the night sky gradient background

initialization UI Button and Info Display clearButton = createButton(...); infoDiv = createDiv(...);

Creates interactive button to clear connections and info display showing star/line counts

Line by Line:

createCanvas(windowWidth, windowHeight);
Creates a canvas that matches the full browser window size, allowing the sketch to fill the entire screen
noStroke();
Disables outlines on all shapes drawn after this point, so stars and shapes will have no borders
colorMode(RGB);
Sets color mode to RGB (Red, Green, Blue), which is the default and what we use throughout the sketch
gradientColors = [color(0, 0, 20), color(0, 0, 40), color(0, 0, 60), color(0, 0, 80)];
Creates an array of four dark blue colors, each progressively lighter, used to create a smooth gradient background
generateStars();
Calls the function that randomly generates 50-80 stars and populates the stars array
clearButton = createButton('Clear Constellations');
Creates a clickable button with the label 'Clear Constellations' and stores it in the clearButton variable
clearButton.position(20, 20);
Positions the button 20 pixels from the left and 20 pixels from the top of the window
clearButton.mousePressed(clearConnections);
Attaches the clearConnections function to the button, so clicking it calls that function
infoDiv = createDiv(`Stars: ${starCount} | Lines: ${lineCount}`);
Creates a text display showing the current number of stars and constellation lines
infoDiv.position(20, 60);
Positions the info display 20 pixels from the left and 60 pixels from the top (below the button)

draw()

draw() runs 60 times per second, creating animation. Each frame, it redraws the background, updates and displays all stars with parallax, draws connection lines, and updates the UI. The parallax effect creates a subtle 3D depth illusion as the mouse moves.

function draw() {
  drawBackgroundGradient();

  for (let star of stars) {
    star.isHovered = star.checkHover(mouseX, mouseY);

    let parallaxX = (mouseX - width / 2) * parallaxStrength;
    let parallaxY = (mouseY - height / 2) * parallaxStrength;

    push();
    translate(parallaxX, parallaxY);
    star.display();
    pop();
  }

  stroke(255, 150);
  strokeWeight(1);
  for (let pair of connections) {
    let parallaxX = (mouseX - width / 2) * parallaxStrength;
    let parallaxY = (mouseY - height / 2) * parallaxStrength;
    line(
      pair[0].x + parallaxX, pair[0].y + parallaxY,
      pair[1].x + parallaxX, pair[1].y + parallaxY
    );
  }
  noStroke();

  infoDiv.html(`Stars: ${starCount} | Lines: ${lineCount}`);
}

๐Ÿ”ง Subcomponents:

for-loop Star Update and Display Loop for (let star of stars) { ... }

Iterates through all stars, checks hover state, applies parallax effect, and displays each star

calculation Parallax Effect Calculation let parallaxX = (mouseX - width / 2) * parallaxStrength; let parallaxY = (mouseY - height / 2) * parallaxStrength;

Calculates how much to offset stars based on mouse position relative to canvas center, creating depth illusion

for-loop Connection Lines Drawing Loop for (let pair of connections) { ... }

Draws white lines connecting pairs of stars that the user has selected

calculation Info Display Update infoDiv.html(`Stars: ${starCount} | Lines: ${lineCount}`);

Updates the on-screen text to show current counts of stars and constellation lines

Line by Line:

drawBackgroundGradient();
Calls the function that draws the dark blue gradient background, clearing the canvas each frame
for (let star of stars) {
Starts a loop that goes through each Star object in the stars array one at a time
star.isHovered = star.checkHover(mouseX, mouseY);
Checks if the mouse is near this star and updates its hover state, which triggers the glow effect
let parallaxX = (mouseX - width / 2) * parallaxStrength;
Calculates horizontal offset: if mouse is right of center, parallaxX is positive; left of center, it's negative
let parallaxY = (mouseY - height / 2) * parallaxStrength;
Calculates vertical offset: if mouse is below center, parallaxY is positive; above center, it's negative
push();
Saves the current drawing state (position, rotation, etc.) so changes only affect the current star
translate(parallaxX, parallaxY);
Moves the coordinate system by the parallax amount, making the star appear to shift with mouse movement
star.display();
Calls the star's display method, which draws it with twinkling and glow effects at the translated position
pop();
Restores the drawing state to what it was before push(), so the next star isn't affected by this translation
stroke(255, 150);
Sets the line color to white with alpha 150 (semi-transparent) for drawing constellation lines
strokeWeight(1);
Sets line thickness to 1 pixel, creating thin delicate lines between connected stars
for (let pair of connections) {
Loops through each pair of stars that the user has connected
line(pair[0].x + parallaxX, pair[0].y + parallaxY, pair[1].x + parallaxX, pair[1].y + parallaxY);
Draws a line from the first star to the second star, adding parallax offset to both endpoints for consistency
noStroke();
Disables stroke for future drawings so the gradient background in the next frame isn't outlined
infoDiv.html(`Stars: ${starCount} | Lines: ${lineCount}`);
Updates the info display text with current counts of stars and lines

Star class

The Star class encapsulates all properties and behaviors of a single star. Each star has its own position, size, and twinkling pattern. The class methods handle visual display with twinkling and glow effects, and collision detection for hover and click interactions. Using a class makes it easy to manage many stars with individual behaviors.

class Star {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.alphaOffset = random(TWO_PI);
    this.isHovered = false;
    this.isSelected = false;
  }

  display() {
    let alpha = map(sin(frameCount * 0.05 + this.alphaOffset), -1, 1, 100, 200);
    fill(255, alpha);

    if (this.isHovered || this.isSelected) {
      drawingContext.shadowBlur = this.size * 2;
      drawingContext.shadowColor = 'rgba(255, 255, 255, 0.8)';
    } else {
      drawingContext.shadowBlur = 0;
    }

    circle(this.x, this.y, this.size);

    drawingContext.shadowBlur = 0;
  }

  checkHover(px, py) {
    return dist(px, py, this.x, this.y) < this.size + 10;
  }

  checkClick(px, py) {
    return dist(px, py, this.x, this.y) < 20;
  }
}

๐Ÿ”ง Subcomponents:

initialization Star Constructor constructor(x, y, size) { ... }

Initializes a new Star with position, size, and properties for twinkling and interaction

calculation Twinkling Alpha Calculation let alpha = map(sin(frameCount * 0.05 + this.alphaOffset), -1, 1, 100, 200);

Creates smooth twinkling by using sine wave to oscillate opacity between 100 and 200

conditional Glow Effect Conditional if (this.isHovered || this.isSelected) { ... }

Applies white shadow glow when star is hovered or selected, removes it otherwise

Line by Line:

constructor(x, y, size) {
Defines the constructor function that runs when a new Star is created with new Star(x, y, size)
this.x = x;
Stores the x-coordinate (horizontal position) of the star
this.y = y;
Stores the y-coordinate (vertical position) of the star
this.size = size;
Stores the diameter of the star in pixels
this.alphaOffset = random(TWO_PI);
Assigns a random phase offset (0 to 2ฯ€) so each star twinkles at a different time, creating natural variation
this.isHovered = false;
Initializes hover state to false; set to true when mouse is near the star
this.isSelected = false;
Initializes selection state to false; set to true when user clicks the star for connection
let alpha = map(sin(frameCount * 0.05 + this.alphaOffset), -1, 1, 100, 200);
Calculates opacity: sin() oscillates between -1 and 1, map() converts this to alpha between 100-200, creating smooth twinkling
fill(255, alpha);
Sets fill color to white (255, 255, 255) with the calculated alpha value for transparency
if (this.isHovered || this.isSelected) {
Checks if the star is being hovered over or has been selected by the user
drawingContext.shadowBlur = this.size * 2;
Enables a blur effect around the star; larger stars get a larger glow radius
drawingContext.shadowColor = 'rgba(255, 255, 255, 0.8)';
Sets the glow color to white with 80% opacity, creating a bright white halo effect
drawingContext.shadowBlur = 0;
Disables the shadow blur effect for non-hovered/non-selected stars
circle(this.x, this.y, this.size);
Draws the star as a circle at its position with its size and current fill color/alpha
drawingContext.shadowBlur = 0;
Resets shadow blur to 0 after drawing to prevent the glow from affecting other elements
return dist(px, py, this.x, this.y) < this.size + 10;
Returns true if the point (px, py) is within 10 pixels of the star's edge, false otherwise
return dist(px, py, this.x, this.y) < 20;
Returns true if the point (px, py) is within 20 pixels of the star's center, false otherwise

generateStars()

generateStars() creates a fresh set of 50-80 randomly positioned stars with varying sizes. It's called during setup() to populate the initial sky, and again when the window is resized. The function resets all state variables to ensure a clean slate.

function generateStars() {
  stars = [];
  connections = [];
  starCount = 0;
  lineCount = 0;
  selectedStar = null;
  for (let star of stars) {
    star.isSelected = false;
  }

  let numStars = floor(random(50, 81));

  for (let i = 0; i < numStars; i++) {
    let x = random(width);
    let y = random(height);
    let size = random(1, 4);
    stars.push(new Star(x, y, size));
    starCount++;
  }
}

๐Ÿ”ง Subcomponents:

initialization Reset All State Variables stars = []; connections = []; starCount = 0; lineCount = 0; selectedStar = null;

Clears all arrays and counters to start fresh

for-loop Star Generation Loop for (let i = 0; i < numStars; i++) { ... }

Creates random stars with random positions and sizes, adding each to the stars array

Line by Line:

stars = [];
Clears the stars array, removing all existing Star objects
connections = [];
Clears the connections array, removing all constellation lines
starCount = 0;
Resets the star counter to 0
lineCount = 0;
Resets the line counter to 0
selectedStar = null;
Clears the selected star variable so no star is highlighted
for (let star of stars) { star.isSelected = false; }
This loop runs on the already-cleared stars array, so it has no effect (defensive programming)
let numStars = floor(random(50, 81));
Generates a random integer between 50 and 80 (inclusive) representing how many stars to create
for (let i = 0; i < numStars; i++) {
Loops numStars times, creating one star per iteration
let x = random(width);
Generates a random x-coordinate between 0 and the canvas width
let y = random(height);
Generates a random y-coordinate between 0 and the canvas height
let size = random(1, 4);
Generates a random star size between 1 and 4 pixels for visual variety
stars.push(new Star(x, y, size));
Creates a new Star object with the random position and size, and adds it to the stars array
starCount++;
Increments the star counter by 1 to track how many stars exist

clearConnections()

clearConnections() is called when the user clicks the 'Clear Constellations' button. It removes all drawn lines and resets the selection state, allowing the user to start drawing a new constellation pattern from scratch.

function clearConnections() {
  connections = [];
  selectedStar = null;
  lineCount = 0;
  for (let star of stars) {
    star.isSelected = false;
  }
}

๐Ÿ”ง Subcomponents:

initialization Clear Data Arrays connections = []; selectedStar = null; lineCount = 0;

Removes all constellation lines and resets selection state

for-loop Deselect All Stars Loop for (let star of stars) { star.isSelected = false; }

Removes highlighting from all stars

Line by Line:

connections = [];
Empties the connections array, removing all constellation lines from the display
selectedStar = null;
Clears the selected star variable so no star is highlighted for connection
lineCount = 0;
Resets the line counter to 0 to reflect that no lines exist
for (let star of stars) {
Loops through every Star object in the stars array
star.isSelected = false;
Sets each star's selection state to false, removing any glow highlighting

mouseClicked()

mouseClicked() handles the core interaction: connecting stars. It implements a state machine with three states: no selection, first star selected, and creating a connection. The function prevents accidental button clicks from interfering with star selection and allows users to deselect by clicking the same star twice.

function mouseClicked() {
  if (mouseX > clearButton.x && mouseX < clearButton.x + clearButton.width &&
      mouseY > clearButton.y && mouseY < clearButton.y + clearButton.height) {
    return;
  }

  let clickedStar = null;
  for (let star of stars) {
    if (star.checkClick(mouseX, mouseY)) {
      clickedStar = star;
      break;
    }
  }

  if (clickedStar) {
    if (selectedStar === null) {
      selectedStar = clickedStar;
      selectedStar.isSelected = true;
    } else if (selectedStar === clickedStar) {
      selectedStar.isSelected = false;
      selectedStar = null;
    } else {
      connections.push([selectedStar, clickedStar]);
      lineCount++;
      selectedStar.isSelected = false;
      clickedStar.isSelected = false;
      selectedStar = null;
    }
  }
}

๐Ÿ”ง Subcomponents:

conditional Button Click Detection if (mouseX > clearButton.x && mouseX < clearButton.x + clearButton.width && mouseY > clearButton.y && mouseY < clearButton.y + clearButton.height) { return; }

Checks if click was on the button and exits early if so, letting the button's own handler manage it

conditional Connection State Machine if (clickedStar) { if (selectedStar === null) { ... } else if (selectedStar === clickedStar) { ... } else { ... } }

Manages the three-state connection process: first star selected, same star deselected, or second star connected

Line by Line:

if (mouseX > clearButton.x && mouseX < clearButton.x + clearButton.width &&
Checks if the mouse click's x-coordinate is within the button's horizontal bounds
mouseY > clearButton.y && mouseY < clearButton.y + clearButton.height) {
Checks if the mouse click's y-coordinate is within the button's vertical bounds
return;
Exits the function early if the click was on the button, allowing the button's own click handler to run
let clickedStar = null;
Initializes a variable to store which star (if any) was clicked
for (let star of stars) {
Loops through each star to check if the click was near it
if (star.checkClick(mouseX, mouseY)) {
Calls the star's checkClick method to see if the click was within 20 pixels of this star
clickedStar = star;
Stores the star that was clicked
break;
Exits the loop immediately since we found the clicked star
if (clickedStar) {
Only processes connection logic if a star was actually clicked
if (selectedStar === null) {
Checks if this is the first star being selected (no star currently selected)
selectedStar = clickedStar;
Stores this star as the first star in the connection pair
selectedStar.isSelected = true;
Highlights the selected star with a glow effect
} else if (selectedStar === clickedStar) {
Checks if the user clicked the same star again (deselection)
selectedStar.isSelected = false;
Removes the glow highlighting from the star
selectedStar = null;
Clears the selection so the user can start a new connection
} else {
The user clicked a different star (second star of the connection)
connections.push([selectedStar, clickedStar]);
Adds a pair containing the first and second star to the connections array
lineCount++;
Increments the line counter to reflect the new constellation line
selectedStar.isSelected = false;
Removes highlighting from the first star
clickedStar.isSelected = false;
Removes highlighting from the second star
selectedStar = null;
Clears the selection so the user can start a new connection

touchStarted()

touchStarted() mirrors mouseClicked() but for touch devices. It uses touchX and touchY instead of mouseX and mouseY. The early return false prevents default browser behaviors that would interfere with the sketch. This function enables the constellation drawer to work on tablets and mobile devices.

function touchStarted() {
  return false;

  if (touchX > clearButton.x && touchX < clearButton.x + clearButton.width &&
      touchY > clearButton.y && touchY < clearButton.y + clearButton.height) {
    return;
  }

  let touchedStar = null;
  for (let star of stars) {
    if (star.checkClick(touchX, touchY)) {
      touchedStar = star;
      break;
    }
  }

  if (touchedStar) {
    if (selectedStar === null) {
      selectedStar = touchedStar;
      selectedStar.isSelected = true;
    } else if (selectedStar === touchedStar) {
      selectedStar.isSelected = false;
      selectedStar = null;
    } else {
      connections.push([selectedStar, touchedStar]);
      lineCount++;
      selectedStar.isSelected = false;
      touchedStar.isSelected = false;
      selectedStar = null;
    }
  }
}

๐Ÿ”ง Subcomponents:

conditional Early Return Statement return false;

Prevents default browser touch behavior (scrolling, zooming) - placed at the very start

conditional Touch Button Detection if (touchX > clearButton.x && touchX < clearButton.x + clearButton.width && touchY > clearButton.y && touchY < clearButton.y + clearButton.height) { return; }

Checks if touch was on the button and exits early if so

Line by Line:

return false;
Immediately returns false to prevent default browser touch behaviors like scrolling or zooming on the page
if (touchX > clearButton.x && touchX < clearButton.x + clearButton.width &&
Checks if the touch's x-coordinate is within the button's horizontal bounds
touchY > clearButton.y && touchY < clearButton.y + clearButton.height) {
Checks if the touch's y-coordinate is within the button's vertical bounds
return;
Exits the function early if the touch was on the button
let touchedStar = null;
Initializes a variable to store which star (if any) was touched
for (let star of stars) {
Loops through each star to check if the touch was near it
if (star.checkClick(touchX, touchY)) {
Calls the star's checkClick method using touchX and touchY coordinates
touchedStar = star;
Stores the star that was touched
break;
Exits the loop since we found the touched star
if (touchedStar) {
Only processes connection logic if a star was actually touched
if (selectedStar === null) {
Checks if this is the first star being selected
selectedStar = touchedStar;
Stores this star as the first star in the connection pair
selectedStar.isSelected = true;
Highlights the selected star with a glow effect
} else if (selectedStar === touchedStar) {
Checks if the user touched the same star again (deselection)
selectedStar.isSelected = false;
Removes the glow highlighting
selectedStar = null;
Clears the selection
} else {
The user touched a different star (second star of the connection)
connections.push([selectedStar, touchedStar]);
Adds the connection pair to the connections array
lineCount++;
Increments the line counter
selectedStar.isSelected = false;
Removes highlighting from the first star
touchedStar.isSelected = false;
Removes highlighting from the second star
selectedStar = null;
Clears the selection for the next connection

drawBackgroundGradient()

drawBackgroundGradient() creates a smooth multi-color gradient background by dividing it into segments. Each segment transitions between two adjacent colors. This technique creates a more sophisticated night sky effect than a single solid color. The function uses the raw Canvas2D API (drawingContext) for advanced gradient control.

function drawBackgroundGradient() {
  for (let i = 0; i < gradientColors.length - 1; i++) {
    let y1 = map(i, 0, gradientColors.length - 1, 0, height);
    let y2 = map(i + 1, 0, gradientColors.length - 1, 0, height);

    let gradient = drawingContext.createLinearGradient(0, y1, 0, y2);

    gradient.addColorStop(0, gradientColors[i]);
    gradient.addColorStop(1, gradientColors[i + 1]);

    drawingContext.fillStyle = gradient;

    rect(0, y1, width, y2 - y1);
  }
}

๐Ÿ”ง Subcomponents:

for-loop Gradient Segment Loop for (let i = 0; i < gradientColors.length - 1; i++) { ... }

Iterates through each pair of adjacent colors to create gradient segments

calculation Linear Gradient Object Creation let gradient = drawingContext.createLinearGradient(0, y1, 0, y2);

Creates a WebGL/Canvas2D gradient object that transitions from one color to another vertically

initialization Gradient Color Stops gradient.addColorStop(0, gradientColors[i]); gradient.addColorStop(1, gradientColors[i + 1]);

Defines the start and end colors for this segment of the gradient

Line by Line:

for (let i = 0; i < gradientColors.length - 1; i++) {
Loops through each pair of adjacent colors (if there are 4 colors, this loops 3 times)
let y1 = map(i, 0, gradientColors.length - 1, 0, height);
Maps the color index i to a y-coordinate, spreading the colors evenly from top to bottom of canvas
let y2 = map(i + 1, 0, gradientColors.length - 1, 0, height);
Maps the next color index to the next y-coordinate, creating the bottom boundary of this segment
let gradient = drawingContext.createLinearGradient(0, y1, 0, y2);
Creates a gradient object that transitions vertically from y1 to y2 (the raw Canvas2D API)
gradient.addColorStop(0, gradientColors[i]);
Adds the current color at position 0 (top) of the gradient
gradient.addColorStop(1, gradientColors[i + 1]);
Adds the next color at position 1 (bottom) of the gradient, creating a smooth transition
drawingContext.fillStyle = gradient;
Sets the fill style to use this gradient instead of a solid color
rect(0, y1, width, y2 - y1);
Draws a rectangle from y1 to y2 that spans the full width, filling it with the gradient

windowResized()

windowResized() is automatically called by p5.js whenever the browser window is resized. It ensures the canvas fills the entire window and regenerates stars so they're distributed properly across the new space. This keeps the sketch responsive on different screen sizes.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  generateStars();
}

๐Ÿ”ง Subcomponents:

initialization Canvas Resize resizeCanvas(windowWidth, windowHeight);

Adjusts the canvas size to match the new window dimensions

initialization Star Regeneration generateStars();

Creates a new set of stars positioned for the new canvas size

Line by Line:

resizeCanvas(windowWidth, windowHeight);
Built-in p5.js function that resizes the canvas to match the current window width and height
generateStars();
Regenerates the star field with new random positions and sizes appropriate for the new canvas size

๐Ÿ“ฆ Key Variables

stars array

Stores all Star objects currently on the canvas. Each element is a Star instance with position, size, and interaction properties.

let stars = [];
connections array

Stores pairs of connected stars. Each element is an array containing two Star objects that should be connected by a line.

let connections = [];
selectedStar object or null

Stores the first star selected by the user for creating a connection. Set to null when no star is selected or after a connection is made.

let selectedStar = null;
clearButton object

Stores the p5.js button object for clearing all constellation lines. Created in setup() and positioned on the canvas.

let clearButton;
infoDiv object

Stores the p5.js div object that displays the current count of stars and constellation lines on the canvas.

let infoDiv;
starCount number

Tracks the total number of stars currently on the canvas. Updated when stars are generated or cleared.

let starCount = 0;
lineCount number

Tracks the total number of constellation lines (connections) currently drawn. Incremented each time two stars are connected.

let lineCount = 0;
gradientColors array

Stores four p5.js color objects representing shades of dark blue. Used to create the night sky gradient background.

let gradientColors = [color(0, 0, 20), color(0, 0, 40), color(0, 0, 60), color(0, 0, 80)];
parallaxStrength number

Controls how much stars and lines shift based on mouse position. Higher values create more dramatic parallax movement.

let parallaxStrength = 0.01;

๐Ÿงช Try This!

Experiment with the code by making these changes:

  1. Change parallaxStrength from 0.01 to 0.05 to make the parallax effect more dramatic - the stars will shift more noticeably as you move your mouse.
  2. Modify the gradientColors array to create different night sky moods: try color(20, 0, 40) for purple, or color(40, 20, 0) for a reddish sunset sky.
  3. In the Star class display() method, change the alpha range from 100-200 to 50-255 to make stars twinkle more dramatically between nearly invisible and fully bright.
  4. Adjust the random star size in generateStars() from random(1, 4) to random(2, 8) to create larger, more visible stars.
  5. Change the glow effect in the Star class by modifying shadowBlur from this.size * 2 to this.size * 4 to create a larger halo around selected stars.
  6. In the draw() function, change the line stroke color from stroke(255, 150) to stroke(100, 200, 255) to make constellation lines blue instead of white.
  7. Modify the checkHover() method tolerance from this.size + 10 to this.size + 30 to make stars glow from farther away.
  8. Try changing the twinkling speed by modifying frameCount * 0.05 to frameCount * 0.1 for faster twinkling or frameCount * 0.02 for slower twinkling.
Open in Editor & Experiment โ†’

๐Ÿ”ง Potential Improvements

Here are some ways this code could be enhanced:

BUG touchStarted() function

The function has 'return false;' at the very beginning, which prevents all code after it from executing. This means touch interaction doesn't work at all - the button check and star connection logic are unreachable.

๐Ÿ’ก Move 'return false;' to the end of the function, after all the connection logic: place it as the last line before the closing brace. This allows the function to process touches first, then prevent default browser behavior.

BUG generateStars() function

The function clears the stars array on line 1, then tries to loop through it on line 6 with 'for (let star of stars)'. Since the array is already empty, this loop never executes and serves no purpose.

๐Ÿ’ก Remove the unnecessary loop entirely: 'for (let star of stars) { star.isSelected = false; }' can be deleted since there are no stars to deselect after clearing.

PERFORMANCE draw() function - parallax calculation

The parallax offset is calculated twice for each connection (once for each endpoint), and the calculation is repeated for every star. This is redundant computation.

๐Ÿ’ก Calculate parallax once at the beginning of draw(): 'let parallaxX = (mouseX - width / 2) * parallaxStrength; let parallaxY = (mouseY - height / 2) * parallaxStrength;' Then reuse these variables for all stars and connections.

STYLE Star class - display() method

The shadowBlur is set to 0 twice: once in the else block and once after the if/else statement. This is redundant.

๐Ÿ’ก Remove the second 'drawingContext.shadowBlur = 0;' line after the if/else block, keeping only the one inside the else clause.

FEATURE mouseClicked() and touchStarted() functions

The code for handling star selection is duplicated between these two functions, making maintenance difficult if changes are needed.

๐Ÿ’ก Create a shared helper function like 'function handleStarSelection(x, y)' that contains the connection logic, then call it from both mouseClicked() and touchStarted().

BUG Star class - checkHover() and checkClick() methods

checkHover() uses a tolerance of 'this.size + 10' while checkClick() uses a fixed 20 pixels. For very small stars (size 1), checkClick is much more forgiving than checkHover, creating inconsistent behavior.

๐Ÿ’ก Make both methods consistent: either use 'this.size + 10' for both, or calculate the click tolerance as 'this.size * 10' to scale with star size.

Preview

AI Constellation Drawer - Create Your Own Star Patterns - xelsed.ai - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of AI Constellation Drawer - Create Your Own Star Patterns - xelsed.ai - Code flow showing setup, draw, star, generatestars, clearconnections, mouseclicked, touchstarted, drawbackgroundgradient, windowresized
Code Flow Diagram