This code is a simple requestAnimationFrame polyfill. Not too interesting, clutters up the rest of the code.

window.requestAnimationFrame =
    window.__requestAnimationFrame ||
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        (function () {
            return function (callback, element) {
                var lastTime = element.__lastTime;
                if (lastTime === undefined) {
                    lastTime = 0;
                }
                var currTime = Date.now();
                var timeToCall = Math.max(1, 33 - (currTime - lastTime));
                window.setTimeout(callback, timeToCall);
                element.__lastTime = currTime + timeToCall;
            };
        })();

Short snippet to determine if the device is a mobile device, or a laptop/pc type of device

window.isDevice = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(((navigator.userAgent || navigator.vendor || window.opera)).toLowerCase()));
var loaded = false;
var init = function () {

Apparently the author was afraid of initializing multiple times?

    if (loaded) return;
    loaded = true;

The canvas is css-styled to be w100% h100%, on mobile phones the author decreases resolution. Probably because of performance reasons


    var mobile = window.isDevice;
    var koef = mobile ? 0.5 : 1;

Initialize the canvas

    var canvas = document.getElementById('heart');
    var ctx = canvas.getContext('2d');
    var width = canvas.width = koef * innerWidth;
    var height = canvas.height = koef * innerHeight;

Alias for Math.random

    var rand = Math.random;

Clear the screen, rgba(0, 0, 0, 1) = black. I'm not sure why he doesn't just use "rgb(0, 0, 0)", or even "black"

    ctx.fillStyle = "rgba(0,0,0,1)";
    ctx.fillRect(0, 0, width, height);

Here begins the interesting part. A parametric equation defines the shape.

The function takes a radian value rad [0, 2PI], and returns an (x, y)-coordinate, which lies within the unit circle. So -1 < x < 1 and -1 < y < 1.

The cool thing is that there's an entire field around parametric equations, meaning there are many interesting shapes to play with!

    var heartPosition = function (rad) {
        //return [Math.sin(rad), Math.cos(rad)];
        return [Math.pow(Math.sin(rad), 3), -(15 * Math.cos(rad) - 5 * Math.cos(2 * rad) - 2 * Math.cos(3 * rad) - Math.cos(4 * rad))];
    };

A helper function that translates and scales a given point. It's noteworthy that the author doesn't use the translation part of this function anywhere within the code.

    var scaleAndTranslate = function (pos, sx, sy, dx, dy) {
        return [dx + pos[0] * sx, dy + pos[1] * sy];
    };

Initializing the canvas on resize event. This code is redundant, as it is exactly the same as the code that initializes the canvas for the first time.

    window.addEventListener('resize', function () {
        width = canvas.width = koef * innerWidth;
        height = canvas.height = koef * innerHeight;
        ctx.fillStyle = "rgba(0,0,0,1)";
        ctx.fillRect(0, 0, width, height);
    });

Use less traces on mobile. Likely for performance reasons

    var traceCount = mobile ? 20 : 50;

Set up an array which will contain the points that will be used as input positions for the particle trails

    var pointsOrigin = [];
    var i;

dr Is the constant used in the initialization for-loop. A lower dr means a higher resolution. Again, mobile gets a more performant value.

    var dr = mobile ? 0.3 : 0.1;

Set up three heart shapes of varying sizes. Loop over a large range of values between [0, 2PI], form an outline of the shape around the unit circle. He then uses scaleAndTranslate(..) to scale up this shape to a desired, and more importantly visible, size.

    for (i = 0; i < Math.PI * 2; i += dr) pointsOrigin.push(scaleAndTranslate(heartPosition(i), 210, 13, 0, 0));
    for (i = 0; i < Math.PI * 2; i += dr) pointsOrigin.push(scaleAndTranslate(heartPosition(i), 150, 9, 0, 0));
    for (i = 0; i < Math.PI * 2; i += dr) pointsOrigin.push(scaleAndTranslate(heartPosition(i), 90, 5, 0, 0));

Alias the length of input as a point-count. It's funny to see he doesn't always use this alias later on in his code, sometimes he does, but sometimes he doesn't.

    var heartPointsCount = pointsOrigin.length;

targetPoints Contains "targets" for the actual points. Similar to how Bret Victor uses "targets" in his iconic talk stop drawing dead fish

    var targetPoints = [];

This function maps the original shape outline. First of all, you can see that each point is translated to the middle of the screen. Additionally, the original location is scaled by some scalar value kx and ky. In practice, this function is used to interpolate between the different "stages" of the animation.

    var pulse = function (kx, ky) {
        for (i = 0; i < pointsOrigin.length; i++) {
            targetPoints[i] = [];
            targetPoints[i][0] = kx * pointsOrigin[i][0] + width / 2;
            targetPoints[i][1] = ky * pointsOrigin[i][1] + height / 2;
        }
    };

The descriptively named e array contains the points that are actually visible on the screen.

    var e = [];

Fill the e array with as many points as there are origin points

    for (i = 0; i < heartPointsCount; i++) {

Give them a completely random starting position

        var x = rand() * width;
        var y = rand() * height;

Each point has...

        e[i] = {

... A starting velocity of (0, 0)

            vx: 0,
            vy: 0,

... An unused property I assume used to be "radius"

            R: 2,

... A starting speed [5, 6], speed affects how "quickly" the velocity grows

            speed: rand() + 5,

... An index for a target point within the targetPoints array. This means that the index of a point != the index of its target, this is odd in my opinion. By changing this line to q: i, the effect remains the exact same.

Note, ~~(..) is a hacky way to write Math.floor(..)

            q: ~~(rand() * heartPointsCount),

... A 1-bit value alternating between -1 and 1, this value later gets added to q randomly, with a 1% chance every frame. This means the target of the point gets changed. I'm not sure why this would be interesting

            D: 2 * (i % 2) - 1,

... A force, this force is used in a physics-calculation between the point and its target down below. Higher force means higher impact of the gravity or whatever the equation may represent.

            force: 0.2 * rand() + 0.7,

... A nastily constructed hsla color

            f: "hsla(0," + ~~(40 * rand() + 60) + "%," + ~~(60 * rand() + 20) + "%,.3)",

... An array of previous positions used to draw a trace

            trace: []
        };

Initializes the array of traces. Oddly enough, the author uses a {x: x, y: y} object instead of a [x, y] array as he used throughout the rest of the code.

On mobile, there are 20 traces, on desktop 50.

        for (var k = 0; k < traceCount; k++) e[i].trace[k] = {x: x, y: y};
    }

Define some constants that will be used later on...

    var config = {

... Still not exactly sure what this is, but if it's set to 1, tracers stop existing. If it's set to 0, time seems to stand still. Anything in between makes the traces last longer it seems. It has something to do with how traces fade out over time.

        traceK: 0.4,

Time is tracked under the time variable. This determines how much time changes per tick

        timeDelta: 0.01
    };

Keeping time.

    var time = 0;

Render loop that gets called every frame

    var loop = function () {

Time management is beyond my comprehension.

There are two variables, time and n.

n is defined every frame to be the negative cosine of time. -cos(time)

Time gets incremented every frame

        var n = -Math.cos(time);

Every frame, targets are set to a different position based on the time. Note that arg[0] and arg[1] are equal, meaning kx === ky.

        pulse((1 + n) * .5, (1 + n) * .5);

This line is an absolute abomination.

// Phases transition like C -> A -> B
if ((Math.sin(time)) < 0) {
    // Phase A
    // this phace is the sucking in phase.
    time += 9 * config.timeDelta;
} else if (n > 0.8) {
    // Phase B
    // this phase is when the heart is at its most expanded.
    // Slowing down time by factor five makes the shape
    // stay expanded for longer
    time += .2 * config.timeDelta;
} else {
    // Phase C
    // this phase happens right after the sucking in phase
    // and stays in this phase until
    // the heart is expanded again
    time += 1 * config.timeDelta;
}
        time += ((Math.sin(time)) < 0 ? 9 : (n > 0.8) ? .2 : 1) * config.timeDelta;

Set bg to .1 to create additional tracing effect

        ctx.fillStyle = "rgba(0,0,0,.1)";
        ctx.fillRect(0, 0, width, height);

Here comes the interesting part, the weird waviness.

For each particle, a physics equation gets run, which is what makes this animation interesting.

        for (i = e.length; i--;) {

u Is an alias for the current point. Why not "point", or "particle", or "tracer" you ask? I don't know.

            var u = e[i];

q Is an alias for the particle's target point, determined by the q-index variable the particle stores in its memory.

            var q = targetPoints[u.q];

Calculate the horizontal, vertical and diagonal distance between the particle and its target

            var dx = u.trace[0].x - q[0];
            var dy = u.trace[0].y - q[1];
            var length = Math.sqrt(dx * dx + dy * dy);

If they're close...

            if (10 > length) {

... and 5% of the time; give the particle a new, random target.

                if (0.95 < rand()) {
                    u.q = ~~(rand() * heartPointsCount);
                }

... The other 95% of the time,

                else {

... 1% chance to flip the "direction" of the neighboring target. Remember, D is a "direction" to pick the next neighboring target

                    if (0.99 < rand()) {
                        u.D *= -1;
                    }

Pick next neighboring target, either left one or right one

                    u.q += u.D;

Make sure that u.q stays within array-index bounds of the targetPoints array.

                    u.q %= heartPointsCount;
                    if (0 > u.q) {
                        u.q += heartPointsCount;
                    }
                }
            }

Increase particle's velocity based on distance to its target

            u.vx += -dx / length * u.speed;
            u.vy += -dy / length * u.speed;

The first trace is the particle's actual position.

            u.trace[0].x += u.vx;
            u.trace[0].y += u.vy;

Slow the particle down according to its force, which turns out to be the force of friction.

            u.vx *= u.force;
            u.vy *= u.force;

Every iteration, each trace point comes n% closer to the previous trace point, this percentage can be configured config.traceK. This kind of makes a retractable chain kind of effect, like an elastic rope, attached to have high tension. Then cut this rope, each point of the rope will try to come to the point next to it.

            for (k = 0; k < u.trace.length - 1;) {
                var T = u.trace[k];
                var N = u.trace[++k];
                N.x -= config.traceK * (N.x - T.x);
                N.y -= config.traceK * (N.y - T.y);
            }

And finally, draw all traces!

            ctx.fillStyle = u.f;
            for (k = 0; k < u.trace.length; k++) {
                ctx.fillRect(u.trace[k].x, u.trace[k].y, 1, 1);
            }
        }

This commented out code shows the target points. It's really helpful in figuring out what's really going on.

I found that this code is a little bit weird, and doesn't show all target points correctly. Instead, this snippet works better. Additionally, notice that the for-loop's syntax is absolutely wtf.

ctx.fillStyle = "rgba(255,255,255,1)";
for (i = targetPoints.length; i--;)
  ctx.fillRect(
    targetPoints[i][0],
    targetPoints[i][1], 
    2, 2);
        //ctx.fillStyle = "rgba(255,255,255,1)";
        //for (i = u.trace.length; i--;) ctx.fillRect(targetPoints[i][0], targetPoints[i][1], 2, 2);

Nothing interesting to see here, just starts the loop.

        window.requestAnimationFrame(loop, canvas);
    };
    loop();
};

var s = document.readyState;
if (s === 'complete' || s === 'loaded' || s === 'interactive') init();
else document.addEventListener('DOMContentLoaded', init, false);