# Give Me a Sine

Tim Hammerquist November 05, 2019 #javascript #mathOne quiet night I was remembering how much fun I had plotting graphics on my TRS-80 Color Computer and lamenting the lack of any decent GUI platforms on modern computers. Specifically, there was one trig function plotter from my CoCo's manual that I thought would be fun to port.

## Polar Dares

The book and code are long gone, but the output looked a little like Fig. 1. But how should we reproduce it now?

I still have echoes of PTSD from wrangling cross-platform JavaScript and
CSS back when it was still called 'DHTML,' but I thought the
Canvas HTML element's 2D API would be a good, erm, *canvas*
for my polar masterpiece! The 2D API provides exactly the Cartesian
plotting interface we need.

First we pick an overall `size`

for the square canvas. Halve it on each axis
to give us an `origin`

.
Then we fetch the `canvas`

element itself and create a 2D `context`

for
it, setting its dimensions while we're at it.

Inside the loop, we plot each value of `t`

(θ) over the range [0, 2π) by
fractional increments. We compute `r`

using the trig function `cos`

. We then
translate the polar coordinates `(r, t)`

to a Cartesian point `(x, y)`

and
plot it relative to our `origin`

.

The canvas routines like `fillRect`

will automatically perform subpixel
antialiasing ("dithering") for fractional coordinates, which can consume
unnecessary CPU. To minimize this, we use a JavaScript bitwise trick to round
to the nearest integer. This isn't crucial in this routine, but may be
helpful soon.

```
const size = 150, origin = size / 2;
const canvas = document.getElementById("graph-js1");
canvas.height = canvas.width = size;
const context = canvas.getContext('2d');
for (let t = 0; t < 2 * Math.PI; t += 0.007) {
const r = 0.9 * origin * Math.cos(2 * t);
const x = origin + r * Math.cos(t);
const y = origin - r * Math.sin(t);
context.fillRect(~~(0.5 + x), ~~(0.5 + y), 1, 1);
}
```

So here is a minimal implementation. Feel free to play around with the code. In the next section, we'll look at how to make it feel more authentically TRS-80.

## Recursive Nostalgia

BASIC's `FOR`

loop translates nicely to JavaScript's `for`

loop. But graphics
plotting on the CoCo was not only imperative and synchronous, but a *lot*
slower! How could I reproduce the satisfyingly tortoise-like plot speed of
the Motorola 6809E (approximated in Fig. 2) with modern
JavaScript?

In most other languages this would be a simple `sleep()`

or
`wait()`

call, but JavaScript is not only inherently asynchronous, but
single-threaded. JavaScript won't let us tie it up in a sleep call because
that one thread has to keep the entire browser page running!

In JavaScript, inserting delays in code is handled using time-outs and callbacks. But to use that we have to rethink how our program is structured. Larger tasks need to be broken down into discrete steps. Each invocation of our function performs one step. Then before we exit the function, we set a "timeout" — a delay after which we perform the next step. And in that intervening time, the browser can handle other things, like network I/O, or user interaction.

So we need to break our larger task ("Plot values from 0 to 2π") into
discrete steps ("Plot one point at regular intervals"). The body of the
function is essentially the same as the body of the `for`

loop above:

**calculate**the`(x, y)`

coordinates for`t`

**plot**the new coordinates- set a timeout to plot the
**next**`t`

Steps (1) and (2) — the body of our `for`

loop above — will
form the body of the new `graphLoop`

callback function, along with a
`setTimeout`

delay just long enough to create that authentic sub-MHz CPU
feel.

The next invocation of the loop won't know what value of `t`

to use, so
we'll have to pass that along with. And since the `setTimeout`

function
doesn't send parameters, we have to create a 0-arity lambda that does
this.

There are also a few quirks of the Klipse widget that I had to work
around. Every code change causes the whole block to be re-evaluated, so
I took advantage of JavaScript's atrocious variable scoping to allow the
outdated `graphLoop`

calls to exit cleanly. Here are the highlights:

`f`

is defined as a`var`

to make it visible across re-evaluations of the code.`f`

is an arrow function, which creates a unique object every evaluation.- On entry to
`graphLoop`

, we check if`f`

has been recreated the code. If so, return without drawing or setting a new time-out. - When the full graph period (
`[0, 2π)`

) is complete, clear the graph and start over at 0.

Here is the final code currently driving Fig. 2.

```
var f = (t) => Math.cos(3 * t);
var paused = false;
const delay = 15;
const size = 300;
const origin = size / 2;
const canvas = document.getElementById("graph-js2");
canvas.height = canvas.width = size;
canvas.onclick = () => (paused = !paused);
const context = canvas.getContext('2d');
context.clearRect(0, 0, size, size);
function graphLoop(_f, t) {
if (_f !== f) return; // exit loop if code modified
if (!paused) {
const r = 0.9 * origin * _f(t);
const x = origin + r * Math.cos(t);
const y = origin + r * Math.sin(t);
context.fillRect(~~(0.5 + x), ~~(0.5 + y), 1, 1);
if (t >= Math.PI) {
context.clearRect(0, 0, size, size);
t = 0;
} else {
t += 0.003;
}
setTimeout(() => graphLoop(_f, t), delay);
} else {
setTimeout(() => graphLoop(_f, t), 200);
}
}
graphLoop(f, 0);
```

## The Challenge

We spent the previous section refactoring our `for`

loop so we could slow the
plotting **down**. In this exercise, we're not going to plot trig functions one
point at a time. We're going to plot about a hundred functions per second,
end-to-end. Let's play with Canvas for a while.

```
const caption = document.getElementById("fig3-caption");
const canvas = document.getElementById('graph-js3');
const graph = canvas.getContext('2d');
const size = 300;
const origin = size / 2;
canvas.height = canvas.width = size;
// let the user stop/start the animation
let paused = true;
canvas.onclick = () => { paused = !paused; };
const graphLoop = (n) => {
caption.innerHTML = `Click box to ${paused ? "start" : "pause"} plot`;
if (paused) {
setTimeout(() => graphLoop(n), 200);
} else {
graph.clearRect(0, 0, size, size);
for (let t = 0; t < 2 * Math.PI; t += 0.004) {
const r = 0.96 * origin * Math.sin(n * t);
const x = origin + r * Math.cos(t);
const y = origin - r * Math.sin(t);
graph.fillRect(~~(0.5 + x), ~~(0.5 + y), 1, 1);
}
setTimeout(() => graphLoop(n + 0.002), 16);
}
};
graphLoop(0);
```

You'll notice lots of `const`

s in the code, because almost none of these variables
will change. E.g., there should be only one `canvas`

element and
only one caption.

The most of the logic is only required to start/stop the animation by clicking
on the canvas. The rest of the code is a loop that does nothing but calculate
the polar coordinates `(r, θ)`

, convert them to Cartesian `(x, y)`

, and plot
them on the canvas.

Note: A previous revision of this post used ClojureScript to render Fig. 3.