Skip to content

Instantly share code, notes, and snippets.

@p3nGu1nZz
Created November 14, 2024 08:41
Show Gist options
  • Save p3nGu1nZz/3acdf783cf14ec21d678db6fc04cc8ee to your computer and use it in GitHub Desktop.
Save p3nGu1nZz/3acdf783cf14ec21d678db6fc04cc8ee to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multiple Wobbly Plasma Bubbles</title>
</head>
<body>
<canvas id="cv"></canvas>
<script>
const cv = document.getElementById('cv'),
ctx = cv.getContext('2d'),
cs = ['255,0,0', '0,255,0', '0,0,255'],
sm = 50,
mv = 5,
sp = {
auto: true,
rate: 180,
check: true,
maxBubs: 10000,
maxTensors: 1000000
};
let dt = true,
ab = false,
rb = false,
fc = 0,
lt = (new Date()).getTime(),
ct = 0,
fps = 0,
tc = 0,
sd = true,
bs = cs.map(c => [cb(c), cb(c)]).flat(),
dbiVisible = true; // Control visibility of the dbi panel
cv.width = window.innerWidth;
cv.height = window.innerHeight;
function cb(c, x, y) {
const r = 20;
x = x || Math.random() * (cv.width - 2 * sm - 2 * r) + sm + r;
y = y || Math.random() * (cv.height - 2 * sm - 2 * r) + sm + r;
const vx = (Math.random() - 0.5) * mv;
const vy = (Math.random() - 0.5) * mv;
return {
x,
y,
r,
vx,
vy,
c
};
}
// UI Layer
const uiLayer = {
modalBackground: {
visible: false,
draw: function(ctx) {
if (this.visible) {
ctx.save();
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(0, 0, cv.width, cv.height);
ctx.restore();
}
}
},
settingsPanel: {
visible: false,
width: window.innerWidth / 3,
height: window.innerHeight / 2,
x: window.innerWidth / 2,
y: window.innerHeight / 2,
buttons: [],
draw: function(ctx) {
if (this.visible) {
// Draw modal background
uiLayer.modalBackground.draw(ctx);
// Draw settings panel
ctx.save();
ctx.translate(this.x - this.width / 2, this.y - this.height / 2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fillRect(0, 0, this.width, this.height);
// Calculate button dimensions
const buttonHeight = (this.height - 8) / 7; // 7 buttons with 1px border space
const buttonWidth = this.width - 2; // Full width minus border space
// Create or update buttons
this.buttons = Array.from({
length: 7
}, (_, index) => ({
text: 'Button ' + (index + 1),
x: 1, // 1px border space
y: 1 + index * (buttonHeight + 1), // 1px border space
width: buttonWidth,
height: buttonHeight,
hover: false
}));
// Draw buttons
this.buttons.forEach(button => {
ctx.fillStyle = button.hover ? '#333' : 'transparent';
ctx.fillRect(button.x, button.y, button.width, button.height);
ctx.fillStyle = button.hover ? '#fff' : '#000';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(button.text, button.x + button.width / 2, button.y + button.height / 2);
});
ctx.restore();
}
}
},
// ... Other UI elements ...
};
// Event listeners for mouse movement and clicks
cv.addEventListener('mousemove', function(e) {
const rect = cv.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
uiLayer.settingsPanel.buttons.forEach((button, index) => {
button.hover = mouseX > button.x + uiLayer.settingsPanel.x - uiLayer.settingsPanel.width / 2 &&
mouseX < button.x + button.width + uiLayer.settingsPanel.x - uiLayer.settingsPanel.width / 2 &&
mouseY > button.y + (index * 60) + uiLayer.settingsPanel.y - uiLayer.settingsPanel.height / 2 &&
mouseY < button.y + (index * 60) + button.height + uiLayer.settingsPanel.y - uiLayer.settingsPanel.height / 2;
});
});
cv.addEventListener('click', function(e) {
uiLayer.settingsPanel.buttons.forEach(button => {
if (button.hover) {
// Close the settings panel
uiLayer.settingsPanel.visible = false;
uiLayer.modalBackground.visible = false;
dbiVisible = true;
}
});
});
document.addEventListener('DOMContentLoaded', (event) => {
const cv = document.getElementById('cv');
const ctx = cv.getContext('2d');
// Set body style to a lighter grey
document.body.style.margin = '0';
document.body.style.overflow = 'hidden';
document.body.style.display = 'flex';
document.body.style.justifyContent = 'center';
document.body.style.alignItems = 'center';
document.body.style.height = '100vh';
document.body.style.backgroundColor = '#131313'; // Lighter grey color
// Set canvas style
cv.style.display = 'block';
cv.style.position = 'absolute';
cv.style.top = '0';
cv.style.left = '0';
// Your existing canvas setup and game logic here
});
// Toggle settings panel visibility and dbi panel
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
if (uiLayer && uiLayer.settingsPanel) {
uiLayer.settingsPanel.visible = !uiLayer.settingsPanel.visible;
uiLayer.modalBackground.visible = uiLayer.settingsPanel.visible;
dbiVisible = !uiLayer.settingsPanel.visible; // Toggle dbi panel visibility
} else {
console.error('uiLayer or settingsPanel is not defined');
}
}
});
// Modify the mouse event listener
cv.addEventListener('mousedown', function(e) {
// Check if the escape window is not visible before allowing bubble creation
if (!uiLayer.settingsPanel.visible && e.button === 0) {
ab = true; // 'ab' is the flag for adding bubbles
}
});
cv.addEventListener('mouseup', function(e) {
// Check if the escape window is not visible before allowing bubble creation
if (!uiLayer.settingsPanel.visible && e.button === 0) {
ab = false; // Reset the add bubble flag
}
});
cv.addEventListener('contextmenu', e => {
e.preventDefault();
rb = true;
setTimeout(() => rb = false, 100);
});
// Modify the keyboard event listener
window.addEventListener('keydown', function(e) {
// Check if the escape window is not visible before allowing bubble creation or removal
if (!uiLayer.settingsPanel.visible) {
if (e.key === 'Delete' || e.key === 'Backspace') {
bs = cs.map(c => [cb(c), cb(c)]).flat(); // Reset bubbles
} else if (e.key === 't' || e.key === 'T') {
dt = !dt; // Toggle draw tensors
} else if (e.key === '+' || e.key === '=') {
ab = true; // Add bubbles
} else if (e.key === '-' || e.key === '_') {
rb = true; // Remove bubbles
} else if (e.key === 'd' || e.key === 'D') {
sd = !sd; // Toggle display info
} else if (e.key === 's' || e.key === 'S') {
sp.auto = !sp.auto; // Toggle auto-spawn
}
}
});
window.addEventListener('keyup', function(e) {
// Check if the escape window is not visible before allowing bubble creation or removal
if (!uiLayer.settingsPanel.visible) {
if (['+', '=', '-', '_'].includes(e.key)) {
ab = rb = false; // Reset flags
}
}
});
function dbi() {
if (!sd) return;
const pd = 8,
pr = 9,
pb = 4,
pt = 7,
bw = 160 + pr,
bh = 125; // No change in size
ctx.fillStyle = 'rgba(0, 0, 0, 0.11)';
ctx.fillRect(pd, pd, bw, bh);
ctx.strokeStyle = '#ccc';
ctx.lineWidth = 1;
ctx.strokeRect(pd, pd, bw, bh);
ctx.font = 'bold 12px monospace';
ctx.fillStyle = 'white';
ctx.shadowColor = 'black';
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
ctx.shadowBlur = 0;
const tp = 18,
lh = 14;
ctx.fillText('FPS: ' + fps, 2 * pd, pd + tp);
ctx.fillText('Bubs: ' + bs.length, 2 * pd, pd + tp + lh);
ctx.fillText('Colors: ' + cs.length, 2 * pd, pd + tp + 2 * lh);
ctx.fillText('Tensors: ' + tc, 2 * pd, pd + tp + 3 * lh);
ctx.fillText('Frame Time: ' + (1000 / fps).toFixed(2) + 'ms', 2 * pd, pd + tp + 4 * lh);
ctx.fillText('Auto-Spawn: ' + (sp.auto ? 'On' : 'Off'), 2 * pd, pd + tp + 5 * lh);
ctx.fillText('Spawn Interval: ' + (sp.rate / 60) + 's', 2 * pd, pd + tp + 6 * lh);
// Display current scale
const currentScale = calculateScale(bs.length).toFixed(2);
ctx.fillText('Current Scale: ' + currentScale, 2 * pd, pd + tp + 7 * lh);
ctx.shadowColor = 'transparent';
}
function calculateScale(totalBubs) {
const minScale = 1 / 7; // Minimum scale factor
const scaleFactor = 1 / (1 + 0.05 * totalBubs); // Diminishing returns scale factor
return Math.max(scaleFactor, minScale);
}
function update() {
ct = (new Date()).getTime();
fps = Math.round(1000 / (ct - lt));
lt = ct;
ctx.clearRect(0, 0, cv.width, cv.height);
tc = 0;
// Calculate the current scale based on the number of bubbles
const scale = calculateScale(bs.length);
// Draw and update bubbles
bs.forEach(b => {
ctx.fillStyle = `rgba(${b.c}, 0.7)`;
ctx.beginPath();
ctx.arc(b.x, b.y, b.r * scale, 0, Math.PI * 2);
ctx.fill();
b.x += b.vx;
b.y += b.vy;
if (b.x > cv.width - b.r * scale - sm || b.x < b.r * scale + sm) b.vx *= -1;
if (b.y > cv.height - b.r * scale - sm || b.y < b.r * scale + sm) b.vy *= -1;
});
// Draw tensors if the limit has not been reached
if (dt && tc < sp.maxTensors) {
for (let i = 0; i < bs.length; i++) {
for (let j = i + 1; j < bs.length; j++) {
if (bs[i].c === bs[j].c) {
tc++;
ctx.strokeStyle = `rgba(${bs[i].c}, 0.5)`;
ctx.beginPath();
ctx.moveTo(bs[i].x, bs[i].y);
ctx.lineTo(bs[j].x, bs[j].y);
ctx.stroke();
}
}
}
}
// Spawn new bubbles if the tensor limit has not been reached
if (sp.auto && fc % sp.rate === 0 && (!sp.check || fps >= 30) && bs.length < sp.maxBubs && tc < sp.maxTensors) {
bs.push(cb(cs[Math.floor(Math.random() * cs.length)]), cb(cs[Math.floor(Math.random() * cs.length)]));
}
// Add or remove bubbles based on user input
if (ab && bs.length < sp.maxBubs && tc < sp.maxTensors) {
bs.push(cb(cs[Math.floor(Math.random() * cs.length)]), cb(cs[Math.floor(Math.random() * cs.length)]));
ab = false; // Reset the add bubble flag
}
if (rb && bs.length > 2) {
bs.splice(-2, 2);
rb = false; // Reset the remove bubble flag
}
dbi();
fc++;
// Draw dbi panel if visible
if (dbiVisible) {
// TODO handle dbi panel drawing code here
}
// Draw UI layer
uiLayer.settingsPanel.draw(ctx);
// ... Other UI elements ...
requestAnimationFrame(update);
}
update();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment