Delicate ASCII Dots
Wave interference patterns rendered with dense Braille Unicode characters. Mouse movement creates rippling waves and clicks spawn expanding rings across the grid.
Generated using Grepped's AI UI component generator — created from scratch, not pulled from a library.
canvasasciibraillewavesinteractivebackgroundreactive
Live Preview — customize or regenerate this in the workspace
Loading preview…
Design Intent
Wave interference patterns displayed as Braille Unicode characters, with mouse-driven ripples and click waves spreading across the grid.
CSS
css
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #000000; }
canvas { display: block; width: 100%; height: 100%; }HTML
html
<canvas id="c"></canvas>Full Source
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #000000; }
canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
var BG = '#000000';
var TC_HEX = '#555555';
var GRID = 80;
var SPEED = 0.75;
var REMOVE_WAVE = true;
function hexToRgbStr(h) {
var c = h.charAt(0) === '#' ? h.slice(1) : h;
return parseInt(c.slice(0,2),16)+', '+parseInt(c.slice(2,4),16)+', '+parseInt(c.slice(4,6),16);
}
var TC = hexToRgbStr(TC_HEX);
var CHARS = '⣧⣩⣪⣫⣬⣭⣮⣯⣱⣲⣳⣴⣵⣶⣷⣹⣺⣻⣼⣽⣾⣿⠁⠂⠄⠈⠐⠠⡀⢀⠃⠅⠘⠨⠊⠋⠌⠍⠎⠏⠑⠒⠓⠔⠕⠖⠗⠙⠚⠛⠜⠝⠞⠟⠡⠢⠣⠤⠥⠦⠧⠩⠪⠫⠬⠭⠮⠯⠱⠲⠳⠴⠵⠶⠷⠹⠺⠻⠼⠽⠾⠿';
var canvas = document.getElementById('c');
var time = 0;
var mouse = { x: 0, y: 0, down: false };
var clickWaves = [];
var bgWaves = [];
var W = 0, H = 0;
function initWaves() {
bgWaves = [];
for (var i=0; i<4; i++) {
bgWaves.push({
x: GRID*(0.25+Math.random()*0.5),
y: GRID*(0.25+Math.random()*0.5),
freq: 0.2+Math.random()*0.3,
amp: 0.5+Math.random()*0.5,
phase: Math.random()*Math.PI*2,
speed: 0.5+Math.random()*0.5
});
}
}
function resize() {
var dpr = window.devicePixelRatio || 1;
W = innerWidth; H = innerHeight;
canvas.width = W*dpr; canvas.height = H*dpr;
canvas.style.width = W+'px'; canvas.style.height = H+'px';
canvas.getContext('2d').scale(dpr, dpr);
}
function clickWaveFx(gx, gy, now) {
var tot = 0;
for (var k=0; k<clickWaves.length; k++) {
var w = clickWaves[k];
var age = now-w.t;
if (age < 4000) {
var dx = gx-w.x, dy = gy-w.y;
var dist = Math.sqrt(dx*dx+dy*dy);
var rad = age/4000*GRID*0.8, ww = GRID*0.15;
if (Math.abs(dist-rad) < ww) {
tot += (1-age/4000)*w.i*(1-Math.abs(dist-rad)/ww)*Math.sin((dist-rad)*0.5);
}
}
}
return tot;
}
function draw() {
var ctx = canvas.getContext('2d');
if (!W || !H) { requestAnimationFrame(draw); return; }
time += SPEED*0.016;
var now = Date.now();
var cw = W/GRID, ch = H/GRID;
var mgx = mouse.x/cw, mgy = mouse.y/ch;
ctx.fillStyle = BG;
ctx.fillRect(0, 0, W, H);
var mouseWave = { x: mgx, y: mgy, freq: 0.3, amp: 1, phase: time*2, speed: 1 };
var allWaves = bgWaves.concat([mouseWave]);
var fs = Math.min(cw, ch)*0.8;
ctx.font = fs+'px monospace';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
for (var gy=0; gy<GRID; gy++) {
for (var gx=0; gx<GRID; gx++) {
var tw = 0;
for (var wi=0; wi<allWaves.length; wi++) {
var wave = allWaves[wi];
var dx = gx-wave.x, dy = gy-wave.y;
var dist = Math.sqrt(dx*dx+dy*dy);
var fo = 1/(1+dist*0.1);
tw += Math.sin(dist*wave.freq - time*wave.speed + wave.phase)*wave.amp*fo;
}
tw += clickWaveFx(gx, gy, now);
var md = Math.sqrt((gx-mgx)*(gx-mgx)+(gy-mgy)*(gy-mgy));
if (md < GRID*0.3) tw += (1-md/(GRID*0.3))*0.8*Math.sin(time*3);
if (Math.abs(tw) > 0.2) {
var nv = (tw+2)/4;
var ci = Math.min(CHARS.length-1, Math.max(0, Math.floor(nv*(CHARS.length-1))));
var op = Math.min(0.9, Math.max(0.4, 0.4+nv*0.5));
ctx.fillStyle = 'rgba('+TC+','+op+')';
ctx.fillText(CHARS[ci], gx*cw+cw/2, gy*ch+ch/2);
}
}
}
if (!REMOVE_WAVE) {
for (var k=0; k<clickWaves.length; k++) {
var cw2 = clickWaves[k];
var age2 = now-cw2.t;
if (age2 < 4000) {
var p = age2/4000;
ctx.beginPath();
ctx.strokeStyle = 'rgba('+TC+','+(1-p)*0.3*cw2.i+')';
ctx.lineWidth = 1;
ctx.arc(cw2.x*(W/GRID), cw2.y*(H/GRID), p*Math.min(W,H)*0.5, 0, Math.PI*2);
ctx.stroke();
}
}
}
requestAnimationFrame(draw);
}
initWaves();
resize();
window.addEventListener('resize', resize);
canvas.addEventListener('mousemove', function(e) {
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX-rect.left; mouse.y = e.clientY-rect.top;
});
canvas.addEventListener('mousedown', function(e) {
mouse.down = true;
var rect = canvas.getBoundingClientRect();
var cw = W/GRID, ch = H/GRID;
clickWaves.push({ x: (e.clientX-rect.left)/cw, y: (e.clientY-rect.top)/ch, t: Date.now(), i: 2 });
var now = Date.now();
clickWaves = clickWaves.filter(function(w){ return now-w.t < 4000; });
});
canvas.addEventListener('mouseup', function(){ mouse.down = false; });
draw();
</script>
</body>
</html>