<script type="text/paperscript" canvas="myCanvas">
    function Ball(r, p, v) {
        this.radius = r;
        this.point = p;
        this.vector = v;
        this.maxVec = 15;
        this.numSegment = Math.floor(r / 3 + 2);
        this.boundOffset = [];
        this.boundOffsetBuff = [];
        this.sidePoints = [];
        this.path = new Path({
            fillColor: {
                hue: Math.random() * 360,
                saturation: 0.3,
                brightness: 1
            },
            blendMode: 'screen'
        });

        for (var i = 0; i < this.numSegment; i ++) {
            this.boundOffset.push(this.radius);
            this.boundOffsetBuff.push(this.radius);
            this.path.add(new Point());
            this.sidePoints.push(new Point({
                angle: 360 / this.numSegment * i,
                length: 1
            }));
        }
    }

    Ball.prototype = {
        iterate: function() {
            this.checkBorders();
            if (this.vector.length > this.maxVec)
                this.vector.length = this.maxVec;
            this.point += this.vector;
            this.updateShape();
        },

        checkBorders: function() {
            var size = view.size;
            if (this.point.x < -this.radius)
                this.point.x = size.width + this.radius;
            if (this.point.x > size.width + this.radius)
                this.point.x = -this.radius;
            if (this.point.y < -this.radius)
                this.point.y = size.height + this.radius;
            if (this.point.y > size.height + this.radius)
                this.point.y = -this.radius;
        },

        updateShape: function() {
            var segments = this.path.segments;
            for (var i = 0; i < this.numSegment; i ++)
                segments[i].point = this.getSidePoint(i);

            this.path.smooth();
            for (var i = 0; i < this.numSegment; i ++) {
                if (this.boundOffset[i] < this.radius / 4)
                    this.boundOffset[i] = this.radius / 4;
                var next = (i + 1) % this.numSegment;
                var prev = (i > 0) ? i - 1 : this.numSegment - 1;
                var offset = this.boundOffset[i];
                offset += (this.radius - offset) / 15;
                offset += ((this.boundOffset[next] + this.boundOffset[prev]) / 2 - offset) / 3;
                this.boundOffsetBuff[i] = this.boundOffset[i] = offset;
            }
        },

        react: function(b) {
            var dist = this.point.getDistance(b.point);
            if (dist < this.radius + b.radius && dist != 0) {
                var overlap = this.radius + b.radius - dist;
                var direc = (this.point - b.point).normalize(overlap * 0.015);
                this.vector += direc;
                b.vector -= direc;

                this.calcBounds(b);
                b.calcBounds(this);
                this.updateBounds();
                b.updateBounds();
            }
        },

        getBoundOffset: function(b) {
            var diff = this.point - b;
            var angle = (diff.angle + 180) % 360;
            return this.boundOffset[Math.floor(angle / 360 * this.boundOffset.length)];
        },

        calcBounds: function(b) {
            for (var i = 0; i < this.numSegment; i ++) {
                var tp = this.getSidePoint(i);
                var bLen = b.getBoundOffset(tp);
                var td = tp.getDistance(b.point);
                if (td < bLen) {
                    this.boundOffsetBuff[i] -= (bLen  - td) / 2;
                }
            }
        },

        getSidePoint: function(index) {
            return this.point + this.sidePoints[index] * this.boundOffset[index];
        },

        updateBounds: function() {
            for (var i = 0; i < this.numSegment; i ++)
                this.boundOffset[i] = this.boundOffsetBuff[i];
        }
    };

    //--------------------- main ---------------------

    var balls = [];
    var numBalls = 18;
    for (var i = 0; i < numBalls; i++) {
        var position = Point.random() * view.size;
        var vector = new Point({
            angle: 360 * Math.random(),
            length: Math.random() * 10
        });
        var radius = Math.random() * 60 + 60;
        balls.push(new Ball(radius, position, vector));
    }

    function onFrame() {
        for (var i = 0; i < balls.length - 1; i++) {
            for (var j = i + 1; j < balls.length; j++) {
                balls[i].react(balls[j]);
            }
        }
        for (var i = 0, l = balls.length; i < l; i++) {
            balls[i].iterate();
        }
    }
</script>
    
<canvas resize="true" id="myCanvas"></canvas>
    
<h1>AWESOME</h1>
    
    
    
    
    
<!-- Link to my website -->
<a id="ajerez" href="http://www.ajerez.es" target="_blank"><img src="https://i.imgur.com/AI4BS2I.png"/></a>
@import url(https://fonts.googleapis.com/css?family=Open+Sans:800);

$color1: #440033;
    
html {
    background-color: #382A32;
    height: 100%;
    padding: 0;
}
body {
    height: 100%;
    padding: 0;
}
h1 {
    color: white;
    width: 100%;
    position: absolute;
    top: 5px;
    text-align: center;
    line-height: 40px;    
    font-size: 13em;
    font-family: 'Open Sans', sans-serif;
    mix-blend-mode: difference;
}
canvas {
    height: 300px;
    width: 90%;
    margin: 0 5%;
    position: relative;
    border: 5px solid #C7D5CD;
    border-radius: 25px;
}
View Compiled
/* 
 * Paper.js and CSS mix-blend-mode
 * A pen by Alberto Jerez
 * www.ajerez.es
 */

// http://paperjs.org/examples/candy-crash/
// https://developer.mozilla.org/es/docs/Web/CSS/mix-blend-mode

External CSS

  1. https://codepen.io/ajerez/pen/PqbdWK.scss

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.9.22/paper-full.min.js