Design Concept
The project “Gravity Dance” aims to create an immersive simulation that explores the graceful and sometimes chaotic interactions within a celestial system.
Storyline
As participants engage with “Gravity Dance,” they will enter a dynamic universe where they can introduce new celestial bodies into a system. Each interaction not only alters the trajectory and speed of these bodies but also impacts the existing celestial dance, creating a living tapestry of motion that mirrors the interconnectivity of space itself.
Interaction Methodology
Users interact with the simulation through simple mouse inputs:
- Clicking on the canvas adds a new celestial body at the point of click.
- Dragging allows users to set the initial velocity of the celestial bodies, giving them tangential speed and direction.
- Hovering provides details about the mass and current velocity of the celestial bodies.
Technical Setup and Code Insights
- Gravitational Physics: Each planet’s movement is influenced by Newton’s law of universal gravitation.
- Cellular Automata: The background is dynamically generated using cellular automata to create a starry night effect. Different shapes and brightness levels represent various types of celestial phenomena.
Design of Canvas
User Interaction Instructions:
- Startup Screen: Instructions are displayed briefly when the user first enters the simulation, explaining how to add and manipulate celestial bodies.
- During Interaction: Cursor changes to indicate different modes (add, drag).
- Feedback: Visual cues such as changes in color or size indicate the mass and speed of the celestial bodies. Textual feedback appears when hovering over a body, showing details.
Current Sketch
Base p5.js Code
let planets = []; let G = 6.67430e-11; // Universal Gravitational Constant let grid, cols, rows; let resolution = 10; // Adjust resolution for visual detail function setup() { createCanvas(windowWidth, windowHeight); cols = floor(width / resolution); rows = floor(height / resolution); grid = Array.from({ length: cols }, () => Array.from({ length: rows }, () => random(1) < 0.1)); frameRate(30); } function draw() { background(0, 20); // Slight fade effect for motion blur // Draw the space-themed cellular automata background drawSpaceCA(); // Draw the central sun fill(255, 204, 0); ellipse(width / 2, height / 2, 40, 40); // Update and display all planets planets.forEach(planet => { planet.update(); planet.display(); }); } function mouseClicked() { let newPlanet = new Planet(mouseX, mouseY, random(5, 20), random(0.5, 2)); planets.push(newPlanet); } class Planet { constructor(x, y, mass, velocity) { this.pos = createVector(x, y); this.mass = mass; this.vel = createVector(velocity, 0); } update() { let force = createVector(width / 2, height / 2).sub(this.pos); let distance = force.mag(); force.setMag(G * this.mass * 10000 / (distance * distance)); this.vel.add(force); this.pos.add(this.vel); } display() { fill(255); ellipse(this.pos.x, this.pos.y, this.mass); } } function drawSpaceCA() { noStroke(); for (let i = 0; i < cols; i++) { for (let j = 0; j < rows; j++) { let x = i * resolution; let y = j * resolution; if (grid[i][j]) { let shapeType = floor(random(3)); // Choose between 0, 1, 2 for different shapes let size = random(3, 6); // Size variation for visual interest fill(255, 255, 255, 150); // Slightly opaque for glow effect if (shapeType === 0) { ellipse(x + resolution / 2, y + resolution / 2, size, size); } else if (shapeType === 1) { rect(x, y, size, size); } else { triangle(x, y, x + size, y, x + size / 2, y + size); } } } } if (frameCount % 10 === 0) { grid = updateCA(grid); // Update less frequently } } function updateCA(current) { let next = Array.from({ length: cols }, () => Array.from({ length: rows }, () => false)); for (let i = 0; i < cols; i++) { for (let j = 0; j < rows; j++) { let state = current[i][j]; let neighbors = countNeighbors(current, i, j); if (state === false && neighbors === 3) { next[i][j] = true; } else if (state === true && (neighbors === 2 || neighbors === 3)) { next[i][j] = true; } else { next[i][j] = false; } } } return next; } function countNeighbors(grid, x, y) { let sum = 0; for (let i = -1; i <= 1; i++) { for (let j = -1; j <= 1; j++) { let col = (x + i + cols) % cols; let row = (y + j + rows) % rows; sum += grid[col][row] ? 1 : 0; } } sum -= grid[x][y] ? 1 : 0; return sum; }
Next Steps
As I continue to develop “Celestial Choreography,” the next phases will focus on refining the physics model to include more complex interactions such as orbital resonances and perhaps collisions. Additionally, enhancing the visual aesthetics and introducing more interactive features are key priorities.