Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save DanielHemmati/e63219a3d03a5f11fd9a9f65463aa6dd to your computer and use it in GitHub Desktop.
Save DanielHemmati/e63219a3d03a5f11fd9a9f65463aa6dd to your computer and use it in GitHub Desktop.
Foam Pit Starfield
<canvas class="fullscreenable"></canvas>
<div data-window-object="starfieldOptions"></div>
// console.clear();
window.starfieldOptions = {};
starfieldOptions.starSize = 17;
starfieldOptions.trajectoryMin = 20;
starfieldOptions.trajectoryMax = 20;
starfieldOptions.fps = 30;
starfieldOptions.blurAmount = 0;
starfieldOptions.focalBlankSize = 0;
starfieldOptions.newStarsPerFrame = 25;
starfieldOptions.redMin = 80;
starfieldOptions.redMax = 120;
starfieldOptions.greenMin = 80;
starfieldOptions.greenMax = 120;
starfieldOptions.blueMin = 80;
starfieldOptions.blueMax = 120;
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const stars = [];
window.addEventListener("resize", resizeCanvas, false);
function addStars(num) {
for (let i = 0; i < num; i++) {
stars.push(getStar());
}
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
stars.length = 0; // empty stars array
drawStuff();
}
function getStar() {
return {
x: canvas.width / 2,
y: canvas.height / 2,
dX:
getRandomInt(
-starfieldOptions.trajectoryMin,
starfieldOptions.trajectoryMax
) +
getRandomInt(0, 9) / 10,
dY:
getRandomInt(
-starfieldOptions.trajectoryMin,
starfieldOptions.trajectoryMax
) +
getRandomInt(0, 9) / 10,
size: starfieldOptions.starSize,
color: `rgb(${getRandomInt(
starfieldOptions.redMin,
starfieldOptions.redMax
)}, ${getRandomInt(
starfieldOptions.greenMin,
starfieldOptions.greenMax
)}, ${getRandomInt(starfieldOptions.blueMin, starfieldOptions.blueMax)})`,
};
}
resizeCanvas();
let lastFps = starfieldOptions.fps;
let handle = setInterval(mainLoop, 1000 / starfieldOptions.fps);
function mainLoop() {
if (starfieldOptions.fps !== lastFps) {
clearInterval(handle);
handle = setInterval(mainLoop, 1000 / starfieldOptions.fps);
}
// delete stars that have moved off screen
const garbageStars = stars.reduce((acc, star, i) => {
if (
star.x > canvas.width ||
star.y > canvas.height ||
star.x < 0 ||
star.y < 0
)
acc.push(i);
return acc;
}, []);
if (garbageStars.length > 0) {
garbageStars.reverse();
garbageStars.forEach((index) => stars.splice(index, 1));
}
// add new star
addStars(starfieldOptions.newStarsPerFrame);
drawStuff();
}
function drawStuff() {
stars.forEach((star) => {
star.x += star.dX; // calculate star's new position
star.y += star.dY;
star.size += ((Math.abs(star.dX) + Math.abs(star.dY)) / 2) * 0.01;
for (let i = starfieldOptions.blurAmount + 1; i > 1; i--) {
const colorStrength = 100 / i;
ctx.fillStyle = `rgb(${colorStrength}, ${colorStrength}, ${colorStrength})`;
ctx.fillRect(
star.x - (star.dX / 2) * i,
star.y - (star.dY / 2) * i,
star.size,
star.size
);
}
ctx.fillStyle = star.color;
ctx.fillRect(star.x, star.y, star.size, star.size);
});
ctx.fillStyle = "rgb(20, 0, 35)";
ctx.fillRect(
canvas.width / 2 - starfieldOptions.focalBlankSize / 2,
canvas.height / 2 - starfieldOptions.focalBlankSize / 2,
starfieldOptions.focalBlankSize,
starfieldOptions.focalBlankSize
);
}
function getRandomInt(min, max) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#Getting_a_random_integer_between_two_values
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
const camelCase = (str) =>
str.replace(/-[a-z]/g, (val) => val[1].toUpperCase());
const kabobCase = (str) =>
str.replace(/[A-Z]/g, (val) => "-" + val.toLowerCase());
const configDiv = document.querySelector("div");
const boundObj = window[configDiv.dataset.windowObject];
Object.entries(boundObj).forEach(([key, value]) => {
const label = document.createElement("label");
const kabobKey = kabobCase(key);
label.textContent = key;
const control = document.createElement("input");
control.setAttribute("type", "number");
control.value = value;
["input", "change"].forEach((event) => {
control.addEventListener(event, (e) => {
boundObj[key] = Number(e.target.value);
});
});
label.htmlFor = control.id = `${kabobCase(
configDiv.dataset.windowObject
)}-${kabobKey}`;
label.appendChild(control);
configDiv.appendChild(label);
});
document.addEventListener("keyup", (e) => {
if (e.code === "KeyH") configDiv.hidden = !configDiv.hidden;
});
body {
margin: 0;
padding: 0
background: red;
}
canvas {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden
}
div {
position: absolute;
top: 10px;
left: 10px;
background: #7777;
color: white;
font-family: monospace;
}
div > * {
display: flex;
justify-content: space-between;
}
div input {
width: 5ch;
margin-left: 2ch;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment