Reputation: 6396
I am trying to plot a sine wave into the canvas: I want to control the frequency like if I say:
10 freq, I want to see 10 periods of oscillating waves with the same amplitude etc.
Currently 10 freq works ok, but If I say 49 frequency, I see a weird change in amplitude. What is causing this and what is my error?
In the example I overlap two different waves to save on space.
let element = document.getElementById('app');
let canvas = document.createElement('canvas');
canvas.width = 320;
canvas.height = 180;
let bounds = element.getBoundingClientRect();
canvas.style.width = bounds.width + 'px';
canvas.style.height = bounds.height + 'px';
element.appendChild(canvas);
let ctx = canvas.getContext('2d');
ctx.strokeStyle = 'red';
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 320, 180);
let points = [];
function fSin(freq, amplitude) {
return x =>
Math.sin(x / 100 * freq * Math.PI * 2) * amplitude;
}
function generate(ff) {
let res = [];
for (let i = 0; i < 100; i++) {
res.push([i, ff(i)]);
}
return res;
}
function drawPoints(points, xscale = 1, yscale = 1) {
ctx.moveTo(points[0][0] * xscale, 90 - points[0][1] * yscale);
for (let i = 1; i < points.length; i++) {
ctx.lineTo(points[i][0] * xscale, 90 - points[i][1] * yscale);
}
ctx.stroke();
}
points = generate(fSin(10, 10));
points = points.concat(generate(fSin(49, 10)));
drawPoints(points, 3.2, 2);
#app {
width: 90vw;
height: 90vh;
margin: auto;
}
#app canvas {
margin: auto;
}
<div id="app"></div>
Upvotes: 1
Views: 880
Reputation: 78840
What you're seeing is just an illusion due to aliasing, the interplay between the sampling rate (where the points are captured) and the sine function. If you were to zoom in and add more in-between points, you'd see that it's still a regular sine function. The omission of the in-between points due to sampling and connecting those samples with lines distorts the actual curve.
For fun, I modified your code a bit and added a slider. In this version, single pixel points are drawn instead, and the slider lets to interactively adjust the frequency. You can see as you increase/decrease the frequency how the samples seem to form weird patterns.
let element = document.getElementById('app');
let canvas = document.createElement('canvas');
canvas.width = 320;
canvas.height = 180;
canvas.style.width = '320px';
canvas.style.height = '180px';
let ctx = canvas.getContext('2d');
element.appendChild(canvas);
function fSin(freq, amplitude) {
return x => Math.sin(x / 100 * freq * Math.PI * 2) * amplitude;
}
let slider = document.createElement('input');
slider.type = 'range';
slider.min = 0;
slider.max = 100;
slider.addEventListener('change', () => {
const frequency = slider.value;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 320, 180);
ctx.strokeStyle = 'red';
ctx.fillStyle = 'red';
const ff = fSin(frequency, 10);
for (let i = 0; i < canvas.width; i++) {
ctx.fillRect(i, 90 - ff(i) * 2, 1, 1);
}
});
element.appendChild(slider);
#app {
width: 90vw;
height: 90vh;
margin: auto;
}
#app canvas {
margin: auto;
}
<div id="app"></div>
Upvotes: 1