diff --git a/README.md b/README.md
index 8c66bc1..6b5e205 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
-#### Code from Frank's Lab YouTube tutorials
+### Code from Frank's Lab YouTube tutorials
Frank Dvorak
https://www.youtube.com/@Frankslaboratory
+#### Constellation Effect
+https://www.youtube.com/watch?v=PoDjjHh931c&t=2457s
+Starts at 00:00
+
diff --git a/constellation_effect/index.html b/constellation_effect/index.html
new file mode 100644
index 0000000..c7dc701
--- /dev/null
+++ b/constellation_effect/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Constellation Effect
+
+
+
+
+
+
+
diff --git a/constellation_effect/script.js b/constellation_effect/script.js
new file mode 100644
index 0000000..624b3bc
--- /dev/null
+++ b/constellation_effect/script.js
@@ -0,0 +1,101 @@
+const canvas = document.getElementById('canvas1');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
+gradient.addColorStop(0, 'white');
+gradient.addColorStop(0.5, 'magenta');
+gradient.addColorStop(1, 'blue');
+ctx.fillStyle = gradient;
+ctx.strokeStyle = 'white';
+
+// stopped at 34:02
+
+
+class Particle {
+ constructor(effect) {
+ this.effect = effect;
+ this.radius = Math.random() * 10 + 5;
+ this.x = this.radius + Math.random() *
+ (this.effect.width - this.radius * 2);
+ this.y = this.radius + Math.random() *
+ (this.effect.height - this.radius * 2);
+ this.vx = Math.random() * 1 - 0.5;
+ this.vy = Math.random() * 1 - 0.5;
+ }
+
+ draw(context) {
+ context.beginPath();
+ context.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ context.fill();
+ }
+
+ update() {
+ this.x += this.vx;
+ if (this.x > this.effect.width - this.radius || this.x < this.radius) { this.vx *= -1; }
+
+ this.y += this.vy;
+ if (this.y > this.effect.height - this.radius || this.y < this.radius) { this.vy *= -1; }
+ }
+}
+
+class Effect {
+ constructor(canvas) {
+ this.canvas = canvas;
+ this.width = this.canvas.width;
+ this.height = this.canvas.height;
+ this.particles = [];
+ this.numberOfParticles = 200;
+ this.createParticles();
+ }
+
+ createParticles() {
+ for(let i = 0; i < this.numberOfParticles; i++) {
+ this.particles.push(new Particle(this));
+ }
+ }
+
+ handleParticles(context) {
+ this.connectParticles(context);
+ this.particles.forEach(particle => {
+ particle.draw(context);
+ particle.update();
+ });
+
+ }
+
+ connectParticles(context) {
+ const maxDistance = 100;
+ for (let a = 0; a < this.particles.length; a++) {
+ for(let b = a; b < this.particles.length; b++) {
+ const dx = this.particles[a].x - this.particles[b].x;
+ const dy = this.particles[a].y - this.particles[b].y;
+ const distance = Math.hypot(dx, dy);
+ if (distance < maxDistance) {
+ context.save();
+ const opacity = 1 - (distance / maxDistance);
+ context.globalAlpha = opacity;
+ context.beginPath();
+ context.moveTo(this.particles[a].x, this.particles[a].y);
+ context.lineTo(this.particles[b].x, this.particles[b].y);
+ context.stroke();
+ context.restore();
+ }
+ }
+ }
+ }
+
+}
+
+const effect = new Effect(canvas);
+effect.handleParticles(ctx);
+
+function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ effect.handleParticles(ctx);
+ requestAnimationFrame(animate);
+}
+
+animate();
+
+
diff --git a/constellation_effect/style.css b/constellation_effect/style.css
new file mode 100644
index 0000000..5f22486
--- /dev/null
+++ b/constellation_effect/style.css
@@ -0,0 +1,12 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+#canvas1 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ background: black;
+}
\ No newline at end of file
diff --git a/particle_template/index.html b/particle_template/index.html
new file mode 100644
index 0000000..c7dc701
--- /dev/null
+++ b/particle_template/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Constellation Effect
+
+
+
+
+
+
+
diff --git a/particle_template/script.js b/particle_template/script.js
new file mode 100644
index 0000000..3fa2dda
--- /dev/null
+++ b/particle_template/script.js
@@ -0,0 +1,75 @@
+const canvas = document.getElementById('canvas1');
+const ctx = canvas.getContext('2d');
+canvas.width = window.innerWidth;
+canvas.height = window.innerHeight;
+const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
+gradient.addColorStop(0, 'white');
+gradient.addColorStop(0.5, 'magenta');
+gradient.addColorStop(1, 'blue');
+ctx.fillStyle = gradient;
+
+
+class Particle {
+ constructor(effect) {
+ this.effect = effect;
+ this.radius = Math.random() * 10 + 5;
+ this.x = this.radius + Math.random() *
+ (this.effect.width - this.radius * 2);
+ this.y = this.radius + Math.random() *
+ (this.effect.height - this.radius * 2);
+ this.vx = Math.random() * 4 - 2;
+ this.vy = Math.random() * 4 - 2;
+ }
+
+ draw(context) {
+ context.beginPath();
+ context.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ context.fill();
+ }
+
+ update() {
+ this.x += this.vx;
+ if (this.x > this.effect.width - this.radius || this.x < this.radius) { this.vx *= -1; }
+
+ this.y += this.vy;
+ if (this.y > this.effect.height - this.radius || this.y < this.radius) { this.vy *= -1; }
+ }
+}
+
+class Effect {
+ constructor(canvas) {
+ this.canvas = canvas;
+ this.width = this.canvas.width;
+ this.height = this.canvas.height;
+ this.particles = [];
+ this.numberOfParticles = 200;
+ this.createParticles();
+ }
+
+ createParticles() {
+ for(let i = 0; i < this.numberOfParticles; i++) {
+ this.particles.push(new Particle(this));
+ }
+ }
+
+ handleParticles(context) {
+ this.particles.forEach(particle => {
+ particle.draw(context);
+ particle.update();
+ })
+ }
+
+}
+
+const effect = new Effect(canvas);
+effect.handleParticles(ctx);
+
+function animate() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ effect.handleParticles(ctx);
+ requestAnimationFrame(animate);
+}
+
+animate();
+
+
diff --git a/particle_template/style.css b/particle_template/style.css
new file mode 100644
index 0000000..5f22486
--- /dev/null
+++ b/particle_template/style.css
@@ -0,0 +1,12 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+#canvas1 {
+ position: absolute;
+ top: 0;
+ left: 0;
+ background: black;
+}
\ No newline at end of file