<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Neon Hexagon Patterns</title>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
background: #000;
}
/* Make the canvas cover the entire viewport */
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
// Grab the canvas and context
var c = document.getElementById('c');
var ctx = c.getContext('2d');
var w = c.width = window.innerWidth;
var h = c.height = window.innerHeight;
// Configuration options controlling the behaviour of the lines
var opts = {
len: 20,
count: 50,
baseTime: 10,
addedTime: 10,
dieChance: 0.05,
spawnChance: 1,
sparkChance: 0.1,
sparkDist: 10,
sparkSize: 2,
color: 'hsl(hue,100%,light%)',
baseLight: 50,
addedLight: 10, // range: [50-10, 50+10]
shadowToTimePropMult: 6,
baseLightInputMultiplier: 0.01,
addedLightInputMultiplier: 0.02,
cx: w / 2,
cy: h / 2,
repaintAlpha: 0.04,
hueChange: 0.1
};
var tick = 0;
var lines = [];
var dieX = w / 2 / opts.len;
var dieY = h / 2 / opts.len;
var baseRad = Math.PI * 2 / 6;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
/**
* Primary animation loop. Clears the canvas with a semi‑transparent fill,
* spawns new lines as needed, and advances existing lines.
*/
function loop() {
window.requestAnimationFrame(loop);
tick++;
ctx.globalCompositeOperation = 'source-over';
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(0,0,0,' + opts.repaintAlpha + ')';
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'lighter';
if (lines.length < opts.count && Math.random() < opts.spawnChance) {
lines.push(new Line());
}
lines.forEach(function(line) {
line.step();
});
}
/**
* Construct a line and initialise its state.
*/
function Line() {
this.reset();
}
Line.prototype.reset = function() {
this.x = 0;
this.y = 0;
this.addedX = 0;
this.addedY = 0;
this.rad = 0;
this.lightInputMultiplier = opts.baseLightInputMultiplier + opts.addedLightInputMultiplier * Math.random();
this.color = opts.color.replace('hue', tick * opts.hueChange);
this.cumulativeTime = 0;
this.beginPhase();
};
/**
* Begin a new movement phase for the line. Chooses a random direction
* and duration. If the line travels too far or randomly dies, it resets.
*/
Line.prototype.beginPhase = function() {
this.x += this.addedX;
this.y += this.addedY;
this.time = 0;
this.targetTime = (opts.baseTime + opts.addedTime * Math.random()) | 0;
this.rad += baseRad * (Math.random() < 0.5 ? 1 : -1);
this.addedX = Math.cos(this.rad);
this.addedY = Math.sin(this.rad);
if (Math.random() < opts.dieChance || this.x > dieX || this.x < -dieX || this.y > dieY || this.y < -dieY) {
this.reset();
}
};
/**
* Advance the line one frame. Draws its current position with a glow.
*/
Line.prototype.step = function() {
this.time++;
this.cumulativeTime++;
if (this.time >= this.targetTime) {
this.beginPhase();
}
var prop = this.time / this.targetTime;
var wave = Math.sin(prop * Math.PI / 2);
var x = this.addedX * wave;
var y = this.addedY * wave;
ctx.shadowBlur = prop * opts.shadowToTimePropMult;
ctx.fillStyle = ctx.shadowColor = this.color.replace('light', opts.baseLight + opts.addedLight * Math.sin(this.cumulativeTime * this.lightInputMultiplier));
ctx.fillRect(opts.cx + (this.x + x) * opts.len, opts.cy + (this.y + y) * opts.len, 2, 2);
// occasional sparks
if (Math.random() < opts.sparkChance) {
ctx.fillRect(
opts.cx + (this.x + x) * opts.len + Math.random() * opts.sparkDist * (Math.random() < 0.5 ? 1 : -1) - opts.sparkSize / 2,
opts.cy + (this.y + y) * opts.len + Math.random() * opts.sparkDist * (Math.random() < 0.5 ? 1 : -1) - opts.sparkSize / 2,
opts.sparkSize,
opts.sparkSize
);
}
};
loop();
// Resize handler to keep canvas full‑screen
window.addEventListener('resize', function() {
w = c.width = window.innerWidth;
h = c.height = window.innerHeight;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
opts.cx = w / 2;
opts.cy = h / 2;
dieX = w / 2 / opts.len;
dieY = h / 2 / opts.len;
});
</script>
</body>
</html>
<canvas id="c"></canvas>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
background: #000;
}
/* Make the canvas cover the entire viewport */
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
}
// Grab the canvas and context
var c = document.getElementById('c');
var ctx = c.getContext('2d');
var w = c.width = window.innerWidth;
var h = c.height = window.innerHeight;
// Configuration options controlling the behaviour of the lines
var opts = {
len: 20,
count: 50,
baseTime: 10,
addedTime: 10,
dieChance: 0.05,
spawnChance: 1,
sparkChance: 0.1,
sparkDist: 10,
sparkSize: 2,
color: 'hsl(hue,100%,light%)',
baseLight: 50,
addedLight: 10, // range: [50-10, 50+10]
shadowToTimePropMult: 6,
baseLightInputMultiplier: 0.01,
addedLightInputMultiplier: 0.02,
cx: w / 2,
cy: h / 2,
repaintAlpha: 0.04,
hueChange: 0.1
};
var tick = 0;
var lines = [];
var dieX = w / 2 / opts.len;
var dieY = h / 2 / opts.len;
var baseRad = Math.PI * 2 / 6;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
/**
* Primary animation loop. Clears the canvas with a semi‑transparent fill,
* spawns new lines as needed, and advances existing lines.
*/
function loop() {
window.requestAnimationFrame(loop);
tick++;
ctx.globalCompositeOperation = 'source-over';
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(0,0,0,' + opts.repaintAlpha + ')';
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = 'lighter';
if (lines.length < opts.count && Math.random() < opts.spawnChance) {
lines.push(new Line());
}
lines.forEach(function(line) {
line.step();
});
}
/**
* Construct a line and initialise its state.
*/
function Line() {
this.reset();
}
Line.prototype.reset = function() {
this.x = 0;
this.y = 0;
this.addedX = 0;
this.addedY = 0;
this.rad = 0;
this.lightInputMultiplier = opts.baseLightInputMultiplier + opts.addedLightInputMultiplier * Math.random();
this.color = opts.color.replace('hue', tick * opts.hueChange);
this.cumulativeTime = 0;
this.beginPhase();
};
/**
* Begin a new movement phase for the line. Chooses a random direction
* and duration. If the line travels too far or randomly dies, it resets.
*/
Line.prototype.beginPhase = function() {
this.x += this.addedX;
this.y += this.addedY;
this.time = 0;
this.targetTime = (opts.baseTime + opts.addedTime * Math.random()) | 0;
this.rad += baseRad * (Math.random() < 0.5 ? 1 : -1);
this.addedX = Math.cos(this.rad);
this.addedY = Math.sin(this.rad);
if (Math.random() < opts.dieChance || this.x > dieX || this.x < -dieX || this.y > dieY || this.y < -dieY) {
this.reset();
}
};
/**
* Advance the line one frame. Draws its current position with a glow.
*/
Line.prototype.step = function() {
this.time++;
this.cumulativeTime++;
if (this.time >= this.targetTime) {
this.beginPhase();
}
var prop = this.time / this.targetTime;
var wave = Math.sin(prop * Math.PI / 2);
var x = this.addedX * wave;
var y = this.addedY * wave;
ctx.shadowBlur = prop * opts.shadowToTimePropMult;
ctx.fillStyle = ctx.shadowColor = this.color.replace('light', opts.baseLight + opts.addedLight * Math.sin(this.cumulativeTime * this.lightInputMultiplier));
ctx.fillRect(opts.cx + (this.x + x) * opts.len, opts.cy + (this.y + y) * opts.len, 2, 2);
// occasional sparks
if (Math.random() < opts.sparkChance) {
ctx.fillRect(
opts.cx + (this.x + x) * opts.len + Math.random() * opts.sparkDist * (Math.random() < 0.5 ? 1 : -1) - opts.sparkSize / 2,
opts.cy + (this.y + y) * opts.len + Math.random() * opts.sparkDist * (Math.random() < 0.5 ? 1 : -1) - opts.sparkSize / 2,
opts.sparkSize,
opts.sparkSize
);
}
};
loop();
// Resize handler to keep canvas full‑screen
window.addEventListener('resize', function() {
w = c.width = window.innerWidth;
h = c.height = window.innerHeight;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
opts.cx = w / 2;
opts.cy = h / 2;
dieX = w / 2 / opts.len;
dieY = h / 2 / opts.len;
});