Skip to content

Instantly share code, notes, and snippets.

@zvakanaka
Created February 19, 2020 02:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save zvakanaka/4366463886437d01568a04a6885522f1 to your computer and use it in GitHub Desktop.
Save zvakanaka/4366463886437d01568a04a6885522f1 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 * .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