HTML5 Canvas Balloon

Today we’re going to draw a balloon on the HTML5 canvas element. Why not, right? Here’s the final result:

HTML5 Canvas Balloon

The balloon shape is basically a circle stretched out in various places. However, it’s not that straightforward to simply draw a circle and then stretch it with the canvas API. Instead, we need to recreate a circle using four cubic Bézier curves:

Circle Bezier Curve Count

After beginning this process, I discovered that it is actually impossible to create a perfect circle using cubic Bézier curves. Instead, you can only get a very close approximation using a constant called “kappa”. So to calculate the length of the “handles” (or the distance from a control point to a corresponding point on the curve), we use the following formula:

var KAPPA = (4 * (Math.sqrt(2) - 1))/3;
var handleLength = KAPPA * radius;

Now we can create a near-perfect circle using four, separate cubic Bézier curves, created with the bezierCurveTo method:

// Begin balloon path

gfxContext.beginPath();

// Top Left Curve
	
var topLeftCurveStartX = centerX - radius;
var topLeftCurveStartY = centerY;
	
var topLeftCurveEndX = centerX;
var topLeftCurveEndY = centerY - radius;
	
gfxContext.moveTo(topLeftCurveStartX, topLeftCurveStartY);
gfxContext.bezierCurveTo(topLeftCurveStartX, topLeftCurveStartY - handleLength,
			topLeftCurveEndX - handleLength, topLeftCurveEndY,
			topLeftCurveEndX, topLeftCurveEndY);
							
// Top Right Curve
	
var topRightCurveStartX = centerX;
var topRightCurveStartY = centerY - radius;
	
var topRightCurveEndX = centerX + radius;
var topRightCurveEndY = centerY;
	
gfxContext.bezierCurveTo(topRightCurveStartX + handleLength, topRightCurveStartY,
			topRightCurveEndX, topRightCurveEndY - handleLength,
			topRightCurveEndX, topRightCurveEndY);
										
// Bottom Right Curve
	
var bottomRightCurveStartX = centerX + radius;
var bottomRightCurveStartY = centerY;
	
var bottomRightCurveEndX = centerX;
var bottomRightCurveEndY = centerY + radius;
	
gfxContext.bezierCurveTo(bottomRightCurveStartX, bottomRightCurveStartY + handleLength,
			bottomRightCurveEndX + handleLength, bottomRightCurveEndY,
			bottomRightCurveEndX, bottomRightCurveEndY);							
	
// Bottom Left Curve
	
var bottomLeftCurveStartX = centerX;
var bottomLeftCurveStartY = centerY + radius;
	
var bottomLeftCurveEndX = centerX - radius;
var bottomLeftCurveEndY = centerY;
	
gfxContext.bezierCurveTo(bottomLeftCurveStartX - handleLength, bottomLeftCurveStartY,
			bottomLeftCurveEndX, bottomLeftCurveEndY + handleLength,
			bottomLeftCurveEndX, bottomLeftCurveEndY);

Once we have our circle in place, we can stretch out the curves to create the balloon shape. I just fiddled around and came out with arbitrary values for the factors used to stretch the shape. For example, I stretched out the bottom using a heightDiff value, calculated as follows:

var heightDiff = (radius * 0.4);

We can now modify the above circle equation to use these values:

var balloonBottomY = centerY + radius + heightDiff

// Updated Bottom Right Curve Value
var bottomRightCurveEndY = balloonBottomY;

// Updated Bottom Left Curve Value
var bottomLeftCurveStartY = baloonBottomY;

To add color to the balloon, I used a radial gradient with, again, some arbitrary values thrown in there:

var gradientOffset = (radius/3);
	
var balloonGradient =
	gfxContext.createRadialGradient(centerX + gradientOffset,
					centerY - gradientOffset, 3,
					centerX, centerY,
					radius + heightDiff);

	balloonGradient.addColorStop(0, this.lightColor.rgbString());
	balloonGradient.addColorStop(0.7, this.darkColor.rgbString());
	
	gfxContext.fillStyle = balloonGradient;
	gfxContext.fill();

To calculate the gradient colors, I found this fantastic color manipulation library, that makes working with colors a snap:

this.baseColor = new Color(color);
this.darkColor = (new Color(color)).darken(0.3);
this.lightColor = (new Color(color)).lighten(0.3);

Check out the full script!

  • Kodak

    THANK YOU! I have been trying to figure this out for MONTHS!

  • Logan

    Glad the article was helpful! Definitely let me know if you have any questions.

  • Diego Amicabile

    Thank you ! I made a little “game” using your balloons 🙂

    http://clickit.pair.com/javascript/balloons/

  • Logan

    Nice! That’s really awesome, Diego; glad to see the balloons getting put to good use. 🙂

  • Guru Prasad

    Thanks a lot Logan, I had created some out of shape balloons for a game and your code helped me make them look awesome. I have added credits in my article here which uses your code to create the balloons. The article was intended to explain how to use Canvas for beginners. I hope you wouldn’t mind.

    http://www.codeproject.com/script/Membership/View.aspx?mid=10705317

    Please look for the points of interest section in my Part 7 article. (This article is currently in review as of this posting, may take upto 24 hours to go live)

  • Logan

    Awesome article, Guru! Thanks for using my balloons and thanks for the credit!

  • Eugene Maning

    I had trouble regarding the Color. It says undefined. :'(

  • loganfranken

    Hey @eugenemaning:disqus! Did you download and include the “color” library (https://github.com/Qix-/color)?

  • Eugene Maning

    Thank you for the quick reply! I forgot to inform that I figured it out 😉 Thank you!

  • loganfranken

    Glad to hear it, @eugenemaning:disqus!