HTML5 Canvas Particle Animation
UPDATE: yes, I know some people out there will see some flickering when viewing the demo. This is because a) I didn’t implement a double buffer, and b) there is no built-in canvas support for double buffering. There is a very simple solution, but I’ll save that for another time.
Having never really been a user of Apple products, I guess it’s not all that surprising that I’ve never been to the MobileMe website. However, after Googling something like the lines of “most popular e-mail providers,” I came across its login page and was blown away.
Holy moly, that looks amazing. Not only is the design great and the colors are just about perfect, but those little sparkles are crazy! They move in what appears to be 3D space, pulsing from behind the cloud to further into the background amongst and into (while highlighting) the iPads and iPhones, and follow your mouse movements for extra fun.
After poking around the source and giving up on 100% comprehension, I decided it might be fun to take a whack at my own thoroughly underwhelming homage to the MobileMe page.
The Setup
For some reason or another, I decided to make a night scene of some mountains and grass for some stars to streak across (I also threw in some parallax, but that’s for another post). Here’s what I’m starting out with prior to any canvas magic:
And here’s the accompanying code to make that happen (it’s pretty simple, but I’m including it anyways so as little is left out as possible):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #container { overflow:hidden; position:relative; } #pixie { z-index:0; background:-moz-linear-gradient(top, #040429, #257eb7); background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#040429), color-stop(100%,#257eb7)); } #mountains, #grass { width:100%; position:absolute; bottom:0; } #mountains { height:156px; z-index:1; background:url(mountains.png) repeat-x 0 0; } #grass { height:62px; z-index:2; background:url(grass.png) repeat-x left 10px; } |
1 2 3 4 5 |
But that’s child’s play compared to what you really came here for: HTML5 canvas! (You should note, though, that the actual height and width attributes of the canvas element must be set in the HTML or dynamically through JavaScript and not just the CSS, otherwise you’ll get something…weird)
The JavaScript
What first needs to happen is setting up the drawing environment. This includes grabbing the appropriate elements and getting the drawing dimensions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var WIDTH = window.innerWidth, HEIGHT = window.innerHeight, MAX_PARTICLES = 100, DRAW_INTERVAL = 60, container = document.querySelector('#container'), canvas = document.querySelector('#pixie'), context = canvas.getContext('2d'), gradient = null, pixies = new Array(); function setDimensions(e) { WIDTH = window.innerWidth; HEIGHT = window.innerHeight; container.style.width = WIDTH+'px'; container.style.height = HEIGHT+'px'; canvas.width = WIDTH; canvas.height = HEIGHT; } setDimensions(); window.addEventListener('resize', setDimensions); |
This ensures that on window resizes, everything gets set again and updated to the new dimensions. You can also tinker with MAX_PARTICLES
and DRAW_INTERVAL
to your liking.
Next up is the Circle
object. It contains the bulk of the logic of how the particles will behave.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | function Circle() { this.settings = {ttl:8000, xmax:5, ymax:2, rmax:10, rt:1, xdef:960, ydef:540, xdrift:4, ydrift: 4, random:true, blink:true}; this.reset = function() { this.x = (this.settings.random ? WIDTH*Math.random() : this.settings.xdef); this.y = (this.settings.random ? HEIGHT*Math.random() : this.settings.ydef); this.r = ((this.settings.rmax-1)*Math.random()) + 1; this.dx = (Math.random()*this.settings.xmax) * (Math.random() < .5 ? -1 : 1); this.dy = (Math.random()*this.settings.ymax) * (Math.random() < .5 ? -1 : 1); this.hl = (this.settings.ttl/DRAW_INTERVAL)*(this.r/this.settings.rmax); this.rt = Math.random()*this.hl; this.settings.rt = Math.random()+1; this.stop = Math.random()*.2+.4; this.settings.xdrift *= Math.random() * (Math.random() < .5 ? -1 : 1); this.settings.ydrift *= Math.random() * (Math.random() < .5 ? -1 : 1); } this.fade = function() { this.rt += this.settings.rt; } this.draw = function() { if(this.settings.blink && (this.rt <= 0 || this.rt >= this.hl)) { this.settings.rt = this.settings.rt*-1; } else if(this.rt >= this.hl) { this.reset(); } var newo = 1-(this.rt/this.hl); context.beginPath(); context.arc(this.x, this.y, this.r, 0, Math.PI*2, true); context.closePath(); var cr = this.r*newo; gradient = context.createRadialGradient(this.x, this.y, 0, this.x, this.y, (cr <= 0 ? 1 : cr)); gradient.addColorStop(0.0, 'rgba(255,255,255,'+newo+')'); gradient.addColorStop(this.stop, 'rgba(77,101,181,'+(newo*.6)+')'); gradient.addColorStop(1.0, 'rgba(77,101,181,0)'); context.fillStyle = gradient; context.fill(); } this.move = function() { this.x += (this.rt/this.hl)*this.dx; this.y += (this.rt/this.hl)*this.dy; if(this.x > WIDTH || this.x < 0) this.dx *= -1; if(this.y > HEIGHT || this.y < 0) this.dy *= -1; } this.getX = function() { return this.x; } this.getY = function() { return this.y; } } |
The first line is some settings for each particle, each of which is named in a pretty self-explanatory manner.
- time_to_live – used to calculate hl–or the half-life–of each particle
- x_maxspeed and y_maxspeed – defines the maximum number of pixels a particle can move each frame
- radius_max – maximum radius a particle can achieve
- rt – used in conjunction with hl to determine how the ratio of maximum speed and full opacity of each particle in each frame
The reset()
function just sets up the particle in a new location if it’s the first iteration or if random is set to true and the move()
function just moves the particle according to the settings (you’ll notice I have them moving faster as they get smaller and more transparent). The good stuff comes in the draw()
function.
Within draw()
, you see that a new path is started for each particle and drawn into a circle with the arc() function. Normally you would use this just to draw an arc, but by supplying 2Π, or the radian equivalent to a full circle, as the fifth argument, you get a circle.
Then you can call createRadialGradient() to fill in the circles we just created with color, which I have set with three color stops at various opacities to really make the particles look like they’re glowing. You should note, though, that the con and g variables are set outside of the Circle object and treated as member variables.
Animating the Particles
Finally, you need to create all of the particles and iterate through each one to move and render:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | for (var i = 0; i < MAX_PARTICLES; i++) { pixies.push(new Circle()); pixies[i].reset(); } function draw() { context.clearRect(0, 0, WIDTH, HEIGHT); for(var i = 0; i < pixies.length; i++) { pixies[i].fade(); pixies[i].move(); pixies[i].draw(); } } setInterval(draw, DRAW_INTERVAL); |
The first part creates each new particle (or Circle()
in this case) and stores it in an array. We then set a function to run at a certain interval based on what frames per second we desire to run at.
The draw()
function simply iterates over the array and calls each necessary function of each particle to animate its movement. It’s necessary, though, to point out the clearRect()
call. Without it, you wouldn’t get an animation of moving stars in the night sky but rather streaking purple circles.
With any luck, you should get something like this:
This is a very base attempt at particle animation with HTML5′s canvas element. You can do a lot of other, crazier things with canvas, but this is a good place to start understanding how it works and how to get animated things moving with JavaScript.
You can also click and drag left and right on the screen to get some parallax going between the mountains and the grass. I’ll go into detail on how to achieve that effect later on in life at some point maybe perhaps.
View the demo (requires you to be running an HTML5-capable browser).
Excellent! So how would you control where the particles originate from and where they burn out? Similar to what Mobile Me did? The particles originate behind the logo and radiate outward. Any idea?
Thanks! It’s pretty simple to get them all to originate from one point (or a region, if you would prefer). In fact, I have the random bool value in the this.s (settings) of the Circle object so you can switch that to false and every point will radiate from the (xdef, ydef) coordinate.
I also have the blink bool value to indicate whether they continue to drift around and fade in again after they fade all the way out or, if set to false, will reset to the origin.
Like I said, pretty simple to get that to happen! However, you’ll notice in the MobileMe site that the particles have random amounts of drift in both the x and y directions so they don’t move in straight lines and have “target tendencies,” meaning they all aim for a particular iDevice in the background. Getting that to happen is a lot more more and easily triples the amount of code necessary to get that working. If you want to know more, though, let me know and I’ll write up something about that, too. Thanks again!
This is absolutely fantastic.
Thanks a lot for writing this piece of code, i couldnt understand how it worked on apple website
No problem. Thanks for reading!
Wow, that looks amazing! I’ve been thinking about creating my own snazzy website in a similar vein am I allowed to use your code or night scene image or is this only intended as an example?
Thanks,
J
Of course you’re allowed! Feel free to use it all if you so desire, I’m just glad you like it. A little head nod my way when you’re done would be great. I’d love to see what you create.
Thank you so much for this awesome script. I am trying to implement it on my website but just found out that it does not work in Internet Explorer, is there some kind of work around?
Thanks,
Tobias
In general, there is an error that returns in just about every browser. It’s an INDEX_SIZE_ERR (DOM Exception 1) which means that somewhere in the code, there is a negative value being put into a canvas method. In this case, on line 110, there are occasions when the circle’s radius will go into negative territory.
In Chrome and whatnot, these errors are tolerated and the animation will continue. In IE, however, the whole thing just halts. The fix is so ridiculously simple and rectifies both the DOM errors and the flashing/stuttering some users have noticed that I don’t know why I never got around to it before. Replace line 110 with the following two lines:
var cr = this.r * newo;
g = con.createRadialGradient(this.x,this.y,0,this.x,this.y,(cr <= 0 ? 1 : cr)); If you’re talking about an IE version that’s less than 9, though, then that’s because those versions don’t support the canvas element. And if you’re talking about the missing background gradient, that’s because I didn’t put in those stupid trashy DX filters. I was hoping instead that IE9 would eventually be patched up to IE10 and use some sort of vendor prefix for CSS3 linear gradients.
Sorry For Being A Total Newb!
But In What Part Of The HTML File,
Do I Stick My Website Content Into?
Because Everytime I Try To Stick It Into A Different Div Tag, It Either Shows At The Bottom Or Top Of The Page.
Thanks
& In This Case Is Their Like Some CSS You Need To Do This?
I guess that’s because there really isn’t a place to do so. This wasn’t really made to be anything to house content, but that doesn’t mean you can’t!
Just add another
#content {
position:absolute;
width:50%;
height:50%;
top:25%;
left:25;
border:1px solid #FFF;
}
That should get you started with a white-bordered box in the center of the screen that is half the width and height of the window. You’ll have to do more stuff if you want some scrolling content, though.
Good luck, and thanks for reading!
[...] de particule basé sur l’animation de simples éléments DIVs. Vous pouvez voir un exemple de moteur de particule en HTML 5 pour approfondir le sujet mais ce n’est pas l’orientation du moteur que je vais vous [...]
how can I add text on the top?
This is absolutely fantastic. You should be proud.
Well done!
I’ve done some benchmarking about Canvas animation on Mobile platform and it was clear that Canvas tecnology was really too heavy to be deployed on mobiles platform.
That’s why i’ve made a particle engine based on DIV animation who is really more speedy. Check it out http://www.flyers-web.org/blog/programmation/how-to-make-a-javascript-particle-engine-238
I noticed when you make the window larger, you are left with white space. Is there a way to make it all expand with the window?
Awesome work btw!
Thanks! And there absolutely is a way!
Unfortunately, it’s not as simple as setting both the container <div> and the <canvas> element with width=”100%” and height=”100%”. You can go ahead and do that with the <div>, but <canvas> requires hard-set widths and heights to draw anything (in Chrome anyways).
What you need to do is add an event listener for window.onresize and set it to something like:
2
3
4
5
6
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
$('#canvas').attr('width',WIDTH).attr('height',HEIGHT);
$('#container').width(WIDTH).height(HEIGHT);
};
Although I think in my code, I actually set the ID for the <canvas> element to “#pixie.” But either way, just set an event listener for window.onresize so you can set the appropriate width and heights for the container, <canvas>, and the application variables (in this case, WIDTH and HEIGHT). Thanks again for reading!
Awesome! Thanks. Still learning javascript . I tweaked it a little since it wasn’t redrawing the stars and came up with this:
window.onresize = function(e){
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
canvas = document.getElementById(‘pixie’);
$(canvas).attr(‘width’, WIDTH).attr(‘height’,HEIGHT);
$(‘#container’).width(WIDTH).height(HEIGHT);
for(var i = 0; i < 100; i++) {
pxs[i] = new Circle();
pxs[i].reset();
}
setInterval(draw,rint);
};
Works perfect for me now
That’s great! Seems like you’re doing fine to me :0)
oops….. scratch that
setInterval(draw,rint);
at the end there. Don’t need to run it twice unless i ant crazy fast stars haha.
A video game that’s realistic? All I? can think of are driving games.
super slick effect, thanks for sharing
How would you go about putting characters/letters in place of particles? Also, how CPU intensive is this?
You can put text at specific coordinates (with font and color set beforehand) with:
context.font = ’12pt Helvetica’;
context.fillStyle = ‘#0000FF’;
context.fillText(‘Your text here’, x, y);
It’s pretty easy. And this shouldn’t be that CPU-intensive. In fact, some browsers already support hardware acceleration, so you may not even have to worry about the CPU.
Hello there, I really wished to tell you that I really bookmarked this article since everyone need to read this. Genuinely wonderful, thank you.
Thanks!
Did somebody have saved https://auth.me.com/my/loginForm/en-us/2E11/javascript-packed.js file ?
guys i someone does – please share it.
I’ll be very grateful to
Is it possible for this to work on iPad’s? I am creating a signup form an would like this animation ontop of a background image but behind a popup (the form) and would like it to move with the form and change width when the screen rotates. Any of this possible?
Definitely possible! According to http://caniuse.com/canvas iOS versions of Safari should be able to work with canvas elements.
As for the rotation, you just need to alter the drawing range with the onorientationchange event, but you should be aware of the order of events as per http://stackoverflow.com/questions/10230064/ipad-canvas-rotation
This is such a great tutorial I was looking for this type of effects everywhere… Great Job!
Hey this is awesome. Any way that I can smooth the animation? I cant figure out what line of code controls the frame rate. thanks!
is choppy in IE 9 and want to smooth out the particle animation on the screen. is it possible?
Hey, is there a way to make the particles come out of two points in the canvas? I’ve tried many things but haven’t been able to figure it out.
Thanks
Thanks very beautiful and useful!
You’re too kind! Thanks for reading!
I implemented this on my site that is http://dev.mvixusa.com but when i run on fire fox my fire fox crashed. Can you please help me for this issue?
It’s really a cool and helpful piece of information. I am glad that you just shared this helpful info with us. Please keep us informed like this. Thanks for sharing.
Great work! Just curious if there is a way to mask the canvas? Instead of having the particles hit the box have them fade out when reaching the edge of the canvas.
Also, is there a way to limit the number of particles displaying. Maybe set max-particle or something?
Thanks for the help
I believe this internet site has got some rattling good info for everyone. “The individual will always be a minority. If a man is in a minority of one, we lock him up.” by Oliver Wendell Holmes.
Unquestionably believe that which you stated. Your favorite justification seemed to
be on the internet the simplest thing to be aware of. I say to you, I definitely get annoyed
while people consider worries that they plainly
don’t know about. You managed to hit the nail upon the top as well as defined out the whole thing without having side effect , people can take a signal. Will likely be back to get more. Thanks
my website … http://www.foreign.go.tz
[...] Tutorial | Demo [...]
[...] Tutorial | Demo [...]
[...] Demo | Tutorial [...]
Hello! Please tell me how to add a second color? So the stars were two colors of white and yellow.
Hi Tim, this is a really great article and it helped me out a lot in my design. Just wanted to thank you and let you know I’ve linked my site http://www.hughanderson.com to yours. I made a few adaptations and I would enjoy it if you would take a look at my site to see what I’ve done. I ended up using window.RequestAnimationFrame for the animation, and adapted the Pixie to make a Sun. I’m also using Div animation for the clouds on my site. Thanks again for posting this useful tutorial. -Hugh
Wow, thanks for this! Your site looks great and I couldn’t be happier with how my stuff helped you out. And I probably should update this to use RequestAnimationFrame, but that wasn’t how things worked way back in the old 2011. Oh well. Thanks again!
Hi,
I am trying to get this to work on a wordpress site as the background. Problem I face is this:
Either it just covers the page completely
or
it works just in the header section.
this is the site:
http://www.ringarose.co.za
Can you perhaps point me in the right direction?
WOuld be so greatly appreciated
Thank you for this great tutorial! I’m just start playing arround with canvas stuff, and it seems to be fun. I want to animate a background:
http://jonnitto.ch/canvas/img/bgFallback.jpg
I’ve added your particels, just with the difference, that they apear always a a specific location.
http://jonnitto.ch/canvas/
Now I want to animate the clouds and the rays. Do you have any ideas or tutorials?
Thankx again
this has to be one of the most amazing tutorials i’ve ever seen Great work! I have a few questions if you could help me out here.
How can i make the particles to move only from bottom to top
Can the particles be replaced with an images instead? (i want to use bubbles for a website i’m working on)
[...] Tutorial | Demo [...]
Some truly quality blog posts on this website , bookmarked .
Great write-up, I’m regular visitor of one’s site, maintain up the excellent operate, and It’s going to be a regular visitor for a lengthy time.
[...] Tutorial | Demo [...]
[...] PARTICLE ANIMATION: Hướng dẫn | Demo 2. ROTATING IMAGE SLIDER: Hướng dẫn | Demo 3. ADAPTIVE THUMBNAIL PILE EFFECT WITH [...]
[...] is to have a beautiful and shining stars Background on a whole page. Using that wondeful tutorial (http://timothypoon.com/blog/2011/01/19/html5-canvas-particle-animation/) I managed to get the perfect background. I use a static canvas to display static stars and an [...]
I am trying to get this to work on our site but this is coming up with Javascript issues http://www.skyblueoceanmedia.com
Would I need to disable the other modules on much homepage?
Thank you in advance.
Skyler x
[…] Tutorial | Demo […]
Hello! Please tell me how to add a second color? So the stars were two colors of white and yellow.
Pleasing you should think of soehnmitg like that
Dag nabbit good stuff you whspeerinappprs!
Big wow? Use matrices and vectors as like you would in normal 3d scene. Probably the most of the code would go below 1000 lines down.
[…] HTML5 Canvas Particle Animation […]
Oh my goodness! Amazing article dude! Thanks, However I am experiencing issues with your RSS. I don’t know the reason why I cannot join it. Is there anybody having identical RSS issues? Anybody who knows the answer will you kindly respond? Thanx!!
[…] http://timothypoon.com/blog/2011/01/19/html5-canvas-particle-animation/ […]
Hello! Awesome demo! I was wondering if there might be a clever way for the page to get the time of day of the visiting local machine/browser and make the BG color(s) on the page a 24 hour cycle? In other words,..
12:00am – 4:00am total night sky, stars clear
4:00am – 5:00am sunlight barely breaking – stars fade a bit
5:00am-7:00am Dawn – stars eventually fade
7:00am – 11:00am Morning brightens as it gets later
11:00am – 4:00pm Full noon sun and starting to fade as it gets later
4:00pm – 6:00pm Late afternoon
6:00pm – 7:00pm Sunset
7:00pm – 9:00pm Early Evening – stars faintly shine
9:00pm – 12:00am Twilight – stars more visible
Night – full view stars
And so-on! Thankz!!
Yeah, I’ve got some ideas, but this question is definitely outside the scope of this post. Feel free to email me if you want to set up a consulting thing.
It’s weird, I’m pretty sure I saw this exact same
content on another site. Did someone steal your work?
I’m pretty sure you’re the original author of this.
I’m now not certain the place you are getting your information, however good topic. I needs to spend some time finding out more or figuring out more. Thank you for excellent information I used to be in search of this info for my mission.|
Your style is so unique in comparison to other folks I have read stuff from.
I appreciate you for posting when you have the opportunity, Guess I will just bookmark this page.