Eyes watching a mouse cursor have being a good GUI demo app since the early versions of X-Window. It shows how to work with color, draw simple shapes and handle mouse coordinates.

Let’s see how easy it is to build a similar app with Collider.JAM and JavaScript.

Eyes

And let’s also extend the original concept by adding the ability to create additional eyes on the mouse click. There was no way to do that in the original X-Window version, but we can do it, since we have the whole canvas at our disposal.

xeyes


.mod sketch project

Let’s create a Collider.JAM .mod project and dna/eye.js file:

mkdir eyes.mod
cd eyes.mod
mkdir dna
touch dna/eye.js

defaults

First, we need to define eye defaults in dna/eyes.js:

// dna/eyes.js

const defaults = {
    x: rx(.5),
    y: ry(.5),
    hr: rx(.02),
    vr: rx(.03),

    color: hsl(.1, .9, .9),

    iris: {
        size: .5,
        shift: .4,
        color: hsl(.42, .4, .5),
    },
    pupil: {
        size: .2,
        shift: .55,
        color: hsl(.05, .3, .1),
    },
}

This object contains the values we use to initialize a newly constructed eye instance.

The properties x and y set the eye center position, hr and vr - horizontal and vertical radiuses for the eye ellipse. The eye color is also defined here.

Functions rx() and ry() calculate relative sizes to screen width and height. So rx(.5) means the half of the screen width and ry(1) means the full-screen height.

An eye has two internal parts - iris and pupil. Each defined by its own property object that determines the size (relative to the eye horizontal radius), color, and shift. The shift is a distance to move the iris or pupil towards the mouse cursor. It is also defined relative to the hr value.

onClone()

We are going to define eye as a simple prototype instead of a constructor or a factory. It means the defined object will be used as a prototype to clone new instances. This process doesn’t create a traditional JavaScript prototype chain, rather produces a fully independent object.

The onClone() function is used in Collider.JAM to initialize a cloned object after construction. Any fancy actions required to setup the object can be performed here.

let id = 0
function onClone(settings) {
    this.name = 'eye' + (++id) // assign a unique name
    augment(this, defaults, settings)
}

The function assigns a unique name like eye1, eye2 etc… Next, it augments the cloned object with default and spawn settings.

draw()

The drawing procedure is the most complex one in this app. But the meaning of each individual command is pretty straightforward.

function draw() {
    // declare local shortcuts to simplify calculations
    const { x, y, hr, vr, iris, pupil } = this

    // eye
    fill(this.color)
    ellipse(x, y, hr, vr)
    
    // calculate direction of the mouse cursor
    const dir = bearing(x, y, mouse.x, mouse.y)
    const d = dist(x, y, mouse.x, mouse.y)

    // iris
    fill(iris.color)
    const ishift = min(hr * iris.shift, d)
    ellipse( x + ishift * cos(dir), y + ishift * sin(dir),
                hr * iris.size, vr * iris.size )

    // pupil
    fill(pupil.color)
    const pshift = min(hr * pupil.shift, d)
    ellipse( x + pshift * cos(dir), y + pshift * sin(dir),
                hr * pupil.size, vr * pupil.size )
}

We use object deconstruction syntax here to simplify calculations, so we don’t have to supply each value with this. prefix.

Three ellipses are drawn here in total. Each preceded by a fill() call to set the corresponding fill color.

There are also some preliminary calculations to determine the shift direction for iris and pupil.

Collider.JAM bearing() and dist() functions are used to calculate the angle and distance to the mouse cursor.

setup()

The setup function creates two eyes in the /lab node:

function setup() {
    // spawn two eyes
    lab.spawn(dna.eye, {
        x: rx(.69),
        y: rx(.25),
    })
    lab.spawn(dna.eye, {
        x: rx(.75),
        y: rx(.25),
    })
}

click()

To create an additional eye on mouse click, introduce a click trap in trap/click.js:

// trap/click.js

function click(e) {
    lab.spawn('eye', {
        x: mouse.x,
        y: mouse.y,
    })
}

Green Eyes on the mouse!

That’s all you need for a functioning Collider.JAM app.

Run it from inside the eyes.mod folder with:

jam play

And eyes in the browser will start to track your cursor :)

Click anywhere on the screen to create more eyes!

More Eyes

You can find the demo app sources on GitHub.