We're live-coding on Twitch! Join us!
Drawing and Animating Jelly Shapes with Canvas Part 2

Drawing and Animating Jelly Shapes with Canvas Part 2

Code Demo Project

In the last tutorial we saw how to draw and animate jelly shapes. This tutorial will be a continuation, and we will see how to create an original slider full of jelly effects. Here is a gif:

We recommend to read the previous tutorial, for understanding the basics to get jelly shapes directly in the browser.

As we announced in the last post, while we build the slider, we will learn how to:

  • Draw more jelly shapes, and text!
  • Use images inside the shapes, not only solid colors.
  • Animate the shapes to show or hide them smoothly.
  • Morph from a jelly shape to another.
  • Make the entire slider responsive.

Let's begin!

Basic HTML and CSS

The HTML and CSS code for our slider is pretty simple. The markup will be just a container element, and a canvas to draw all the jelly things:

<div class="jelly-container">
    <canvas class="jelly-canvas"></canvas>

And the CSS (SCSS) will be the following:

/* General styles */

html, body {
  width: 100%;
  height: 100%;
  margin: 0;

body {
  background-color: #106CD9;
  overflow: hidden;

/* Jelly slider */

$width: 1300px;
$height: 600px;

// Setting dimensions
.jelly-container, .jelly-canvas {
  width: $width;
  height: $height;

// Centering
.jelly-container {
  position: relative;
  left: 50%;
  top: 50%;
  margin-left: - $width / 2;
  margin-top: - $height / 2;

As you can see, the markup and styles required are very straightforward. All the hard work will be in the JavaScript. But first, we need to draw our paths in a vector editor, let's see how to do it!

Drawing SVG paths

We have used a vector editor to draw the SVG paths we need. We have exported the final result to a SVG file, getting something like this:

better.dev Get Started w/ JavaScript for free!

As you can see, our paths are overlapping, and there's nothing wrong with that, because only one text and one arrow will be displayed at a time, while the others remain hidden. In addition, the circles around the big shape have been drawn to guide us when positioning the elements on the canvas.

Note also that each letter (and any other shape) must consist of a single closed path. We can use whatever font we want (turn to paths), but we need to make sure we are respecting this condition.

Finally, we have setted id attributes to every shape in the vector editor, so we can select them later from JavaScript.

Making the shapes jelly with Canvas

As we can see in the first part of this tutorial, to initialize the jelly shapes we only need this line of code:

var jelly = new Jelly('.jelly-canvas', options);

The hard work here is to populate the options to draw the paths we want with the effects we want.

As we will be working with many objects (the options for each shape), we will use a function to extend and object as we need. You can find it in the JavaScript file, and we will use it a lot. So, let's see how we are setting the basic options for each type of shape:

/* Setup options */

// Base options for every shape
var baseOptions = {
    svg: 'jelly.svg',
    pointsNumber: 16

// Options for each circle
var optionsCircle = extend({}, baseOptions, {
    paths: '#jelly-circle',
    maxDistance: 40,
    mouseIncidence: 25,
    maxIncidence: 25

// Options for each image (big shapes)
var optionsImage = extend({}, baseOptions, {
    paths: '#jelly-image',
    maxDistance: 150,
    mouseIncidence: 50,
    maxIncidence: 50

// Options for each text
var optionsText = extend({}, baseOptions, {
    color: 'rgba(0, 0, 0, 0.6)',
    maxDistance: 15,
    mouseIncidence: 20,
    maxIncidence: 20

// Options for each arrow
var optionsArrow = extend({}, baseOptions, {
    color: 'rgba(255, 255, 255, 0.5)',
    pointsNumber: 30,
    maxDistance: 40,
    mouseIncidence: 20,
    maxIncidence: 20

Then we are going to populate an array with all the options for each shape. We have described the entire process for better understanding:

var items = 5;      // Number of items in the slider
var current = 2;    // Index of current item
var busy = false;   // To check if there is an animation in progress
var options = [];   // Array to populate the options

// Positions for each circle, obtained with the help of the vector editor
var circlePositions = [
    {x: -530, y: 5},
    {x: -330, y: -205},
    {x: 0, y: -285},
    {x: 330, y: -205},
    {x: 530, y: 5}

// Function to build the options for an specific item
function buildOptions(i) {
    var index = (i + 1);
    var isCurrent = i === current;

    // Options for each text, arrow and image, using the base options and the index
    var text = extend({}, optionsText, {paths: '#jelly-text-' + index + ' path'});
    var arrow = extend({}, optionsArrow, {paths: '#jelly-arrow-' + index});
    var image = extend({}, isCurrent ? optionsImage : optionsCircle, {image: 'img/image-' + index + '.jpg'});

    // If not the current item, set circle in the position defined, hide text, and hide arrow
    if (!isCurrent) {
        extend(image, circlePositions[i]);
        extend(text, {hidden: true});
        extend(arrow, {hidden: true});

    // Push all of these to the options array

// Build the options for each item
for (var i = 0; i < items; i++) {

And with this we should have all our jelly shapes working in the browser :)

Handling events and adding interactivity

In the previous tutorial we have seen how we can handle the hover event to get the hovered item. We will not explain again how to do this, just keep in mind that we are saving the index of the hovered item in the variable hoverItem.

Let's see the code below to understand how to handle the click events and perform animations accordingly.

canvas.addEventListener('click', function () {
    // Checking if an item is hovered and it is not busy
    if (hoverItem >= 0 && !busy) {
        busy = true;

        // Hide current text and arrow, and morph the big shape to a circle with the right position
        if (current !== undefined) {
            jelly.hide({i: current * 3, maxDelay: 400});
            jelly.hide({i: current * 3 + 1, maxDelay: 400});
            jelly.morph(extend({i: current * 3 + 2}, optionsCircle, circlePositions[current]));

        // For the clicked item, show the hovered text and arrow, and morph the circle into the big shape
        jelly.show({i: hoverItem * 3, maxDelay: 400});
        jelly.show({i: hoverItem * 3 + 1, maxDelay: 400});
        jelly.morph(extend({i: hoverItem * 3 + 2}, optionsImage, {x: 0, y: 0}));

        // Update current item and release busy after some time to prevent malfunction
        current = hoverItem;
        setTimeout(function () { busy = false; }, 500);

Surely you've noticed that we are using the functions show, hide and morph to perform the animations. All of these functions receive a set of options, and they have been properly documented in the github repository.

Making it responsive

So far, our slider should work very well. But if we see it in a small screen, we will notice a clear lack of responsiveness. Fortunately, we can solve this issue with the following JavaScript code:

/* Scale the container accordingly to windows/device size, making it responsive */

var jellyContainer = document.querySelector('.jelly-container');

function resize() {
    var scale = Math.min(
        window.innerWidth / jellyContainer.clientWidth,
        window.innerHeight / jellyContainer.clientHeight
    jellyContainer.style.transform = 'scale(' + scale + ')';

window.addEventListener('resize', resize);



And with all these we should have an original jelly slider in the browser :)

See the DEMO, and get the full code on Github.

Please note that this demo is very experimental, with a high consumption of resources. So, use jelly shapes moderately as they can kill the performance of the web page.

We hope you find it fun and inspiring!

Like this article? Follow @lmgonzalves on Twitter