PROBLEM: I cannot get the waves to appear on the left side of the canvas as well.
QUESTION: How do I mirror the waves I have already created? Or I need to figure out how to draw the lines on the left side as well.
SEARCHED/TRIED: search for Googled: javascript mirror art on canvas2d. I have such a specific project which makes it hard to search.
INFO/background: The code snippet included has been modified by me. Credit goes to @Blindman67 I am under the impression that using canvas is better on your processor because it uses your graphics card. I am not sure the better method mirroring or drawing the bars on the left side.
Inspiration:
YouTube:
Link to video: https://www.youtube.com/watch?v=-475XHUgkiQ&t=22782s&ab_channel=APMusic
My Original Code: from CodePen.io
Link to codepen.io: https://codepen.io/ac0ua/pen/BaReZEx
const barsDefault = { // colors Must be 4 char CSS hash color eg `#F00` is red
count: 60,
meanSize: 16, // number of bars per mean bucket (mean is used for bg glows)
glowDecay: 0.3, // pulls down mean bucket values
glows: [
["#F00", "#40F", "#00F", "#00F", "#00F", "#00F"],
["#0F0", "#0F8", "#06F", "#00F", "#00F", "#00F"],
["#40F", "#00F", "#00F", "#00F"],
],
centerRadius: 35,
decay: 0.1,
innerHeightPad: 10, //canvas padding or wave padding/margin
color: "#FFF", // Must be 4 char CSS hash color
barWidth: 0.6, // fraction of bar space to contain bar (default:.6)
centerBarWidth: 3, // (default: 10)
centerInset: 0.9, // as fraction of radius (default .9)
centerInsetWidth: 0.2, // as fraction of radius (default .2)
centerInsetAng: 0.5, // as fraction of 180 deg (default .5)
centerRotateMax: 10, // as fraction of 180 deg (default 10)
barFeq: 1 / 8, // odds per frame of new position (default: 1 / 8)
barPeakWidth: 10, // max number of bars to spread movement. Must be whole number (default: 10)
barPeakWidthMin: 2, // min number of bars to spread movement. Must be whole number >= 1 (default: 2)
barPeakSlide: 3, // twice Max rate that level drive slides along bars
barLevel: 1 / 12, // Max bar volume add
barLevelComp: 1 / 2, // drives bar volume compressor n. Is logarithmic. Where 0 < n < Infinity, louder as nit gets smaller
levelDriveDecay: 0.01, // rate that level drivers decay Must be 0 < n <= 1
levelDriveMin: 0.05, // min level drive. Below this level driver turns off. Must be 0 < n
glowOffsetRate: 0.001, // as radians per frame
glowAlpha: 0.3,
};
// Math Helper util
//retating the center circle
const polarArg = (ang, dist, x = 0, y = 0) => ([Math.cos(ang) * dist + x | 0, Math.sin(ang) * dist + y | 0]);
const polarArgoffset = (ang, dist, x = 0, y = 0) => ([Math.cos(ang) * dist + x | 0, (Math.sin(ang) * -1) * dist + y /1 | 0]);
const eCurve = (v, p = 2, vp) => v < 0 ? 0 : v > 1 ? 1 : (vp = v ** p) / (vp + (1 - v) ** p);
// const eCurve = (v, p = 2, vp) => v < 0 ? 0 : v > 1 ? 1 : (vp = v ** p) / (vp + (1 - v) ** p);
const PI = Math.PI;
const TAU = PI * 2;
requestAnimationFrame(renderLoop);
const bars = Bars(canvasContainer);
addEventListener("resize", bars.resize);
function renderLoop() {
bars.update();
bars.draw();
requestAnimationFrame(renderLoop);
}
function Bars(container, config = {}) {
var bounds, w, h;
const canvas = document.createElement("canvas")
const ctx = canvas.getContext("2d");
container.appendChild(canvas);
function resize() {
bounds = container.getBoundingClientRect();
w = bounds.width;
h = bounds.height;
Object.assign(Object.assign(canvas, {
width: w,
height: h
})
.style, {
position: "absolute",
top: "0px",
left:"0px"
});
ctx.lineCap = ctx.lineJoin = "arrow"; //endings of the wave lengths (default "round")
centerBar = ctx.createLinearGradient(0, 0,w, 0);
centerBar.addColorStop(0, setup.color + "0");
centerBar.addColorStop(0.2, setup.color + "A");
centerBar.addColorStop(0.4, setup.color + "F");
centerBar.addColorStop(0.6, setup.color + "F");
centerBar.addColorStop(0.8, setup.color + "A");
centerBar.addColorStop(1, setup.color + "0");
}
const setup = {
...barsDefault,
// ...config,
};
resize(); //calls resize
const levels = new Array(setup.count).fill(0);
const levelDrivers = [];
const meanSize = setup.count /setup.meanSize | 0;
const means = new Array(setup.count / meanSize | 0).fill(0);
var rotate = 0, rotatePos = 0, rotateDelta = 0, totalLevel = 0, glowAng1 = 0, glowAng2 = 0;
// create center bar gradient
var centerBar;
return Object.freeze({
update() {
// updateCenter();
updateBars();
},
draw() {
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, w, h);
ctx.fillStyle = setup.color;
drawBars();
},
resize,
});
function updateBars() {
var i = setup.count, pos, level, move, bPW, head = 0, tail = 0;
const decay = 1 - setup.decay;
totalLevel = 0; // global to Bars
while (i--) {
means[i / meanSize | 0] += levels[i];
totalLevel += levels[i];
levels[i] *= decay;
}
if (Math.random() < setup.barFeq) {
const pos = setup.count * Math.random() | 0;
const level = Math.random() ** setup.barLevelComp * setup.barLevel;
levelDrivers.push([
pos,
level,
(Math.random() - 0.5) * setup.barPeakSlide,
Math.random() * (setup.barPeakWidth - setup.barPeakWidthMin) + setup.barPeakWidthMin | 0,
]);
}
while (head < levelDrivers.length) {
const lDrive = levelDrivers[head++];
[pos, level, move, bPW] = lDrive; // bPW for bar Peak Width
lDrive[0] = pos += move;
pos |= 0;
pos >= 0 && pos < setup.count && (levels[pos] = Math.min(1, levels[pos] + level));
i = 1;
while (i < bPW) {
const levelAdd = eCurve(1 - i / bPW) * level;
pos + i < setup.count && (levels[pos + i] = Math.min(1, levels[pos + i] + levelAdd));
pos - i >= 0 && (levels[pos - i] = Math.min(1, levels[pos - i] + levelAdd));
i++;
}
lDrive[1] = (level *= 1 - setup.levelDriveDecay);
if (level > setup.levelDriveMin && pos + bPW >= 0 && pos - bPW < setup.count) {
levelDrivers[tail++] = lDrive;
}
}
levelDrivers.length = tail;
}
function drawBars()
{
const cw = w * 0.5,
ch = h * 0.5;
var i = setup.count,
j = 0,
xl = cw - setup.centerRadius,
xr = cw + setup.centerRadius,
x = 0;
const cr = setup.centerRadius,
damp = setup.count * 0.3,
xs = xl / setup.count,
xw = xs * setup.barWidth,
hScale = ch - setup.innerHeightPad;
const output = document.querySelector("h1");
ctx.fillStyle = centerBar;
ctx.strokeStyle = centerBar;//setup.color;
ctx.lineWidth = xw;
ctx.beginPath();
while (i--) {
const lh = (j < damp ? 1-((damp-j) / damp) ** 2 : 1) * levels[i] * hScale;
if(lh > 0.5) {
setTimeout(function() { }, 1600);
const r = x * 4 + cr;
const lhr = lh / r;
output.innerText = (polarArgoffset(lhr + 0.0001, r, xl + cr - x * 3, ch)) + "\n";
output.innerText += (polarArg(lhr + 0.0001, r, xl + cr - x * 3, ch));
// add lhr + 0.0001 to start angle to fix miter artifact
ctx.moveTo(...polarArg(lhr + 0.0001, r, xl + cr - x * 3, ch))
ctx.lineTo(...polarArgoffset(lhr + 0.0001, r, xl + cr - x * 3, ch))
// ctx.arc (xl + cr - x * 3, ch, r, lhr, -lhr, true);
// ctx.lineTo(...polarArgoffset(PI + lhr + 0.0001, r, xr - cr + x * 3, chh))
// ctx.moveTo(...polarArg(PI + lhr + 0.0001, r, xr - cr + x * 3, ch))
// ctx.arc (xr - cr + x * 3, ch, r, PI + lhr, PI - lhr, true);
}
j ++;
x += xs;
}
ctx.stroke();
}
}
#container {
background-image: radial-gradient(circle at 40px 40px, rgba(157,227,255,1) 0%, rgba(60,152,240,1) 24%, rgba(44,73,105,1) 56%, rgba(12,1,66,1) 94%, rgba(0,0,1,1) 100%);
font-family: Arial Black;
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
}
h1, h2 {
text-align: center;
font-size: xx-large;
}
h1 { color: #fff;font-size: xxx-large}
h2 { color: #8F4}
#canvasContainer {
position: absolute;
top: 29%;
left: 0px;
height: 70%;
width: 100%;
}
canvas {
mix-blend-mode: lighten;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<div id="container">
<h1>Audio <span>Candy</span></h1>
<h2>2D Canvas Animation</font>
<div id="canvasContainer"></div>
</div>
<script src="script.js"></script>
</body>
</html>