Every now and then I am lucky enough to create a pen that people really like. SVG Lava Lamp is one of them - in fact I've had so many people asking about it that I decided to write this blog post/tutorial.

On the face of it there does seem to be an awful lot of code in this post - there isn't really. I just keep pasting in the same code with incremental additions so there's a lot of repetition.

Also the headings get progressively more and more ridiculous - they're simply there for my own amusement and sadly add very little to the meaning of the sections. If they're off-putting or if English isn't your first language then I profusely apologise.

If you're looking for the Adobe Illustrator file for this tutorial (or related links) go down here.

Stop Jabbering

Without wishing to sound like a total tit, this pen didn't take that long to make - maybe two or three hours, in part because I had Sara Farnsworth's awesome shot as a reference. Check out her Dribbble stuff - it's super cool and has inspired me on many occasions.

Ok I'll stop jabbering.

The main bulk of the effort needed to create this animation takes place outside the browser in a drawing program. The actual JavaScript animation is only around 15-20 lines of code.

You'll need a vector program like Adobe Illustrator (AI). Yes it's bloody expensive but there are others (Sketch[$99] which is Mac only, InkScape[free!]) but I use AI as it's what I'm used to.

The only other thing you really need is GSAP's TweenMax and that's free.

Unsheathe Thy Mighty Sword (Well, Pen. Actually, Mouse)

First off, create an artboard that's 600x600. I don't know why I always use these dimensions but I do and they seem to work well for me.

Now draw the glass shape - we'll use this shape for both the actual glass and the mask for the blobs (actually it's a clipPath which is subtely different from an SVG mask). It doesn't matter what colour the glass is because we'll remove it in the code later. More on that in a bit.





Now draw the top and bottom bits - notice there's a very faint gradient from the orange colour to black on both top and bottom parts. This will create the illusion that the lamp's light is spilling out onto it a little.





We'll also need to draw all the wax blobs and fill them with a radial gradient - just draw four or five different sizes and shapes. To make these draw a circle with the Ellipse tool and then use an AI warp filter like Filter > Warp > Arc Lower then mess about with the Bend and Distortion settings (make sure Preview is on!).





Once you've drawn your blobs make sure the dark part of the gradient is at the top - remember the light source of the lava lamp is underneath. Now position them all roughly around the top of the lava lamp (if you've ever seen a real one you may have noticed the wax blobs disappear up inside the top cone bit).

Don't forget to also draw a larger, flatter blob and place that at the bottom with the top poking out just above the bottom bowl bit. Whilst this isn't a truly accurate representation of a lava lamp as all the wax melts and floats about it's poetic/animatic license that contributes to the gooey effect when we eventually add the SVG blur and colour matrix filters that create that goo effect when combined.





Oh I almost forgot - I drew a table top in the there (black rectangle at the bottom) - otherwise it looks like it's floating in mid-air.

Finally I made a rectangle with a radial gradient that sits behind everything that will act as a glow. I tried this originally with an SVG glow filter on the glass but the performance was terrible and the effect wasn't quite what I wanted in terms of its shape so this was an aesthetically suitable solution.





Now, if you happened to attend my Reasons To Be Creative session in September in Brighton, UK then thanks for coming - it was fun! You might remember me going on and on about a very important tip when copy and pasting your graphics from AI's artboard into a text editor (or CodePen or whatever). I'll repeat myself here because it's a really useful tip.

The Really Useful Tip

You can save SVGs to an .svg file from AI which is all well and good but it rather annoyingly outputs every layer whether it's visible or not. It also has a tendency to generate a load of inline crap I don't want.

Usually I want to pick and choose the required layers so I select the ones I want, copy them and paste them into my text editor. The problem with this method is that when you make your selection and copy and paste into an editor, what you paste is an entire SVG with a width and height that is the width and height of the bounding box of your selection.

When you select elements in AI and copy and paste into an editor, what you paste is an entire SVG with a width and height that is the width and height of the bounding box of your selection.

That is to say, if you had a 600x600 artboard with your lava lamp glass and top and bottom graphics and you wanted to create a 600x600 SVG with those elements positioned correctly within it when you copy it over, you'd think that by selecting those elements and copy/pasting them into an editor would give you the result you wanted. But it doesn't because it just doesn't know the context (artboard) from which it was pasted; instead it takes the width and height of the selection and uses that as the resulting SVG's width and height.





Notice the Transform panel - it says the selection has a width of 131.304px and a height of 304px. When you copy and paste this into an HTML page the SVG looks like this.





(I've just realised I haven't drawn it very well - the top line of the base is wonky! GANNON, SIT IN THE BIN!).

Note the highlighted SVG width (131.3) and height(304) - the generated SVG is the same size as the selection meaning you've lost the 600x600 artboard and they're not sitting in the middle like they should.

Draw a rectangle in AI that's the same dimensions as your artboard and always select that along with the elements you want when copying to an editor

The simple solution (yes I know you should never say something is simple when doing a tutorial but in this particular instance it really is simple) is to draw a rectangle in AI that's the same dimensions as your artboard and always select that along with the elements you want when copying to an editor. From now on I will refer to this as the 'context' layer. You can always discard the context layer from the SVG code later but it's necessary in the copy/paste process so that the generated SVG has the 600x600 dimensions of the artboard (because the context layer is the same size as the artboard). With me?

So now this...





...generates this in HTML...





That's better.

So now when you need to add, say, the wax blobs you can select them all and then add the context layer to the selection (by holding down Shift and clicking it) - this keeps them in their correct position.

I tend to use SublimeText as a go-between to clean up my SVGs and cherry pick the bits I want. It's not strictly necessary but I like Sublime's Find/Replace/Regex functionality so it's easy for me to perform repetitive clean-up tasks. You could just as easily paste it all into CodePens HTML pane.

A quick note about layers - I've named all my layers, even if I don't intend to animate them. The layer names become ids in SVG (albeit with annoying numbers and underscores added by AI). Naming layers makes it easier to identify them in the page later on.





As I mentioned above, AI insists on adding ids to layers - it would be more useful if it added class names - that way you could have several layers that were all related by a class. Still that's the way it is and in my session at Reasons I made a point of pasting the SVG data into Sublime and changing all 'id=' to 'class='. This is actually a bit dangerous because references to gradients, filters, clipPath (and many others) require you to reference them by their id so I've (mostly, not always) stopped changing the id to class now. If I do need to group elements with a class name then I add the class and keep the id as well.

The Process, In Plain English

We're ready to move the graphics into the page. Before we do I just want to explain in plain English how the animation will work. I do this a lot before I build anything or write any code - I describe to myself what will happen visually and technically.

Use the glass (main lamp body bit) for two things. First we'll display it visually as the graphic with a low opacity (so it's like glass). Second we'll use the same graphic as a clipPath on a group that will contain the wax blobs.

on a group that will contain the wax blobs. We'll put the wax blobs in a group and apply a couple of filters to the group (which will affect all members of that group). The infamous 'goo' effect is a Gaussian blur filter whose alpha contrast is boosted right up using a ColorMatrix filter (I'm not going to go into too much detail with the goo effect as it's somewhat outside the scope of this, but this article explains it very well with GIFs).

We'll animate the blobs up and down on a loop (with a TweenMax instance) - they'll each have a random duration (within certain parameters) and they will all wait for a random amount of time before they repeat their loop.

instance) - they'll each have a random duration (within certain parameters) and they will all wait for a random amount of time before they repeat their loop. We'll add the TweenMax tweens to a main TimelineMax instance but have them all starting at different times.

tweens to a main instance but have them all starting at different times. Finally we'll jump ( TimelineMax.seek(100); ) to about 100 seconds in the main timeline so that the lava lamp is in full swing every time you load it.

Copy, Paste, Clean

At last we can get it out of AI and into an editor to tidy it up.

We could select all the layers we want which and copy them in. There's no problem with this other than the fact you will end up a lot of code that all looks quite similar and can become quite confusing quite quickly if you do it all in one go. I tend to copy stuff over in pieces and build it up.

So first hide all layers. Then show the 'bg' layer and select it (make sure you twirl down the layer group to show the actual graphic layer) - this is the rectangle with a gradient that will be the glow behind the lamp and will sit at the back/bottom of the SVG. In this instance we don't need to add the 600x600 context layer to the selection (to maintain the SVG dimensions) because this 'bg' layer is 600x600. Now copy it into an editor and it'll look something like this:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> </defs> <radialGradient id="bg_3_" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> <rect id="bg_2_" fill="url(#bg_3_)" width="600" height="600"/> </svg>

So our first copy/paste has created our SVG at 600x600 because the graphic we pasted was 600x600. We'll add any new graphics to this SVG from now on.

AI has decided to give the id the name bg_2 which is ugly and stupid. Remove the _2 . It's also generated the gradient for it and given that the id bg_3 . We'll change that to a more readable name like bgGrad - we also now need to change the fill reference for the <rect> to fill="url(#bgGrad)" .

I like to keep my gradients, filters, clipPaths and masks in the definitions tag <defs></defs> . These are usually referenced things or things that aren't visual in their own right. So we'll move the gradient tag there - you should have something that looks like this.

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> </svg>

Ok we're winning (wow that sounded really patronising - sorry).

Now create a new pen in CodePen and paste this from your editor into the HTML pane. This article assume you have a CodePen account - if you don't you are a bad, bad person and you may join me in the bin.

I won't show you what this should look like because it's the same as the last code snippet.

Next up go back to AI and hide the bg layer - we don't need it any more. We do, though, need the 600x600 context layer back on (the horrible purple coloured one). Did I mention this layer is really important? No? THEN SIT IN THE BIN FOR NOT LISTENING.

We'll add the glass in next. Select the glassShape layer and add the context layer to the selection. Now copy/paste it into Sublime. Don't worry about keeping the last stuff you pasted in - we just use the editor as a go-between.

It should look like this:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> </defs> <rect id="context_1_" fill="#523CBF" width="600" height="600"/> <path id="glassShape_1_" fill="#F21458" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/> </svg>

Delete the _1 from glassShape and also delete the ` fill attribute (I'll explain in a minute why). Now copy just that path data into your pen - you should just select this bit:

<path id="glassShape" fill="#F21458" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/>

You might remember that we are going to use this in two places - as the glass graphic and as the clipPath (to mask the wax blobs).

So in CodePen we need to add in a <clipPath> tag in the <defs> tag and put this glass shape inside. We also need to give the clipPath an id (we'll call it glassMask ). Your SVG code in the CodePen HTML pane should look like this:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> <clipPath id="glassMask"> <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/> </clipPath> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> </svg>

You shouldn't see anything in the pen's preview area other than the bg gradient. That's because we have just added in that glass shape to the <defs which is not visible. In order to use that glassShape visually we'll add it below the bg gradient using <use> . We'll also just drop the opacity down so the bg shows through.

It's worth noting here that even though glassShape has been placed inside a <clipPath> tag we can still reference and use it for other things.

So your code will look like this:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> <clipPath id="glassMask"> <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/> </clipPath> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> </svg>

Note that I've added fill="#EB7619" to the <use> instance.

We removed the fill attribute from the original glassShape earlier and that is so that we can style a <use> instance. If we'd left the fill on the glassShape we would not be able to override that fill in subsequent <use> instances.

Now your preview pane will show the glass and the bg together like this:





Are we all still awake? Right, somebody open a window.

Next we'll add the table top, base and top bit. Select them along with the context layer and copy them into your text editor like this.

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> </defs> <rect id="context_1_" fill="#523CBF" width="600" height="600"/> <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174"> <stop offset="1.530612e-002" style="stop-color:#000000"/> <stop offset="0.233" style="stop-color:#050202"/> <stop offset="0.4808" style="stop-color:#120706"/> <stop offset="0.7421" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <polygon id="lampTop_1_" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379"> <stop offset="5.102041e-003" style="stop-color:#000000"/> <stop offset="0.2251" style="stop-color:#050202"/> <stop offset="0.4754" style="stop-color:#120706"/> <stop offset="0.7394" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <path id="lampBot_1_" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5 c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop_1_" y="470" width="600" height="130"/> </svg>

Again we'll delete the _1 from the main ids of lampTop , lampBot and tableTop . Don't worry about renaming the gradient reference (e.g. fill="url(#lampBot_2_)" ) - it's more work than it's worth to rename the gradient and the fill references.

Now, copy and paste both gradient tags into the <defs> tag in the CodePen HTML pane. Your code should look like this:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> <clipPath id="glassMask"> <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/> </clipPath> <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174"> <stop offset="1.530612e-002" style="stop-color:#000000"/> <stop offset="0.233" style="stop-color:#050202"/> <stop offset="0.4808" style="stop-color:#120706"/> <stop offset="0.7421" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379"> <stop offset="5.102041e-003" style="stop-color:#000000"/> <stop offset="0.2251" style="stop-color:#050202"/> <stop offset="0.4754" style="stop-color:#120706"/> <stop offset="0.7394" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> </svg>

Now copy lampTop , lampBot and tableTop and paste those below the glass shape <use> tag.

Your code should look like this:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> <clipPath id="glassMask"> <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/> </clipPath> <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174"> <stop offset="1.530612e-002" style="stop-color:#000000"/> <stop offset="0.233" style="stop-color:#050202"/> <stop offset="0.4808" style="stop-color:#120706"/> <stop offset="0.7421" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379"> <stop offset="5.102041e-003" style="stop-color:#000000"/> <stop offset="0.2251" style="stop-color:#050202"/> <stop offset="0.4754" style="stop-color:#120706"/> <stop offset="0.7394" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> <polygon id="lampTop" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <path id="lampBot" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5 c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop" y="470" width="600" height="130"/> </svg>

Your preview pane should now show those new graphics like this:







Blob Time!

Go back to AI and hide the layers we just copied (apart from the context layer).

Show all the wax blob layers including the bottom one ( botBlob ) and select them along with the context layer.

Copy and paste them into your go-between editor. It'll look like this (if you ain't cocked it up).

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> </defs> <rect id="context_1_" fill="#523CBF" width="600" height="600"/> <radialGradient id="blob0_2_" cx="292" cy="171.5" r="56.5354" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob0_1_" fill="url(#blob0_2_)" d="M326.2,149.5c-5,19.2-21.4,29.2-37.8,26.6c-16.5-2.9-33.4-12.9-37.1-26.6 c-3.8-13.6,12.5-32.1,37.8-34.9C314.4,111.8,331.3,130.4,326.2,149.5z"/> <radialGradient id="blob1_2_" cx="297" cy="167.5" r="37.2156" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob1_1_" fill="url(#blob1_2_)" d="M320.5,146.4c-4.4,10.1-16.4,20.2-26.8,25.3c-10.4,5.2-22.4-2.9-26.8-15.2 c-4.4-11.6,7.6-20.4,26.8-25.3C312.9,126.3,324.9,135.6,320.5,146.4z"/> <radialGradient id="blob2_2_" cx="294" cy="157" r="23" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob2_1_" fill="url(#blob2_2_)" d="M278,147.7c2.7-7.1,9.4-15.7,15.4-16.4c5.9-0.4,12.6,8.5,15.4,16.9 c2.7,8.4-4.2,14.9-15.4,14.2C282.2,161.5,275.3,154.8,278,147.7z"/> <radialGradient id="botBlob_2_" cx="284.5" cy="421.5" r="53.521" gradientTransform="matrix(-2.802637e-002 -0.9996 5.9976 -0.1682 -2235.533 776.7669)" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="botBlob_1_" fill="url(#botBlob_2_)" d="M354,381.2c6.8,3.4,5.4,7.4-5.6,10.4c-10.7,3.1-31.1,5.1-54.4,8.4 c-23.3,3.3-43.7,0.8-54.4-2.4c-11-3.4-12.4-7.6-5.6-13.8c6.8-7,18.9-14.6,29.6-17.4c11-3.3,20.6-1.8,30.4-1.4 c9.8,0.4,19.4,5.1,30.4,8.3C335.1,376.8,347.2,378.6,354,381.2z"/> <radialGradient id="blob3_2_" cx="291.9382" cy="167.4587" r="41.0767" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob3_1_" fill="url(#blob3_2_)" d="M312.7,147.3c-2.1,16.4-15.3,27.2-23.2,25.3c-8.1-1.8-12.6-13-14.8-24.9 c-1.9-11.8,2.7-22.7,14.8-25.3C301.5,119.6,314.7,130.8,312.7,147.3z"/> <radialGradient id="blob4_2_" cx="306.5" cy="155" r="14.109" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <path id="blob4_1_" fill="url(#blob4_2_)" d="M317.8,147.4c-1,8.2-9.8,10.3-13.8,9.3c-4-0.9-6.5-3-7.6-8.9c-1-5.9,2.3-8.5,8.4-9.8 C310.8,136.6,318.8,139.1,317.8,147.4z"/> </svg>

What a bloody mess.

Ok we need to clean this up. Let's delete the _1 from the each path's ids (e.g. <path id="blob4_1_" becomes <path id="blob4" etc).

And as we did before, select all the gradient tags and copy/paste them into the <defs> tag in the CodePen HTML pane. No need to rename their ids and fill references.

Next select the blob paths (there should be six) and copy/paste those into the CodePen HTML pane under the last path (which should be the tableTop rectangle).

If you haven't had a mental breakdown by now at the sheer boringness of this bit then your code in CodePen should look like this:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> <clipPath id="glassMask"> <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/> </clipPath> <!-- LAMP GRADIENTS--> <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174"> <stop offset="1.530612e-002" style="stop-color:#000000"/> <stop offset="0.233" style="stop-color:#050202"/> <stop offset="0.4808" style="stop-color:#120706"/> <stop offset="0.7421" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379"> <stop offset="5.102041e-003" style="stop-color:#000000"/> <stop offset="0.2251" style="stop-color:#050202"/> <stop offset="0.4754" style="stop-color:#120706"/> <stop offset="0.7394" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <!--END LAMP GRADIENTS--> <!-- BLOB GRADIENTS --> <radialGradient id="blob0_2_" cx="292" cy="171.5" r="56.5354" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob1_2_" cx="297" cy="167.5" r="37.2156" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob2_2_" cx="294" cy="157" r="23" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="botBlob_2_" cx="284.5" cy="421.5" r="53.521" gradientTransform="matrix(-2.802637e-002 -0.9996 5.9976 -0.1682 -2235.533 776.7669)" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob3_2_" cx="291.9382" cy="167.4587" r="41.0767" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob4_2_" cx="306.5" cy="155" r="14.109" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <!-- END BLOB GRADIENTS--> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> <polygon id="lampTop" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <path id="lampBot" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5 c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop" y="470" width="600" height="130"/> <path id="blob0" fill="url(#blob0_2_)" d="M326.2,149.5c-5,19.2-21.4,29.2-37.8,26.6c-16.5-2.9-33.4-12.9-37.1-26.6 c-3.8-13.6,12.5-32.1,37.8-34.9C314.4,111.8,331.3,130.4,326.2,149.5z"/> <path id="blob1" fill="url(#blob1_2_)" d="M320.5,146.4c-4.4,10.1-16.4,20.2-26.8,25.3c-10.4,5.2-22.4-2.9-26.8-15.2 c-4.4-11.6,7.6-20.4,26.8-25.3C312.9,126.3,324.9,135.6,320.5,146.4z"/> <path id="blob2" fill="url(#blob2_2_)" d="M278,147.7c2.7-7.1,9.4-15.7,15.4-16.4c5.9-0.4,12.6,8.5,15.4,16.9 c2.7,8.4-4.2,14.9-15.4,14.2C282.2,161.5,275.3,154.8,278,147.7z"/> <path id="botBlob" fill="url(#botBlob_2_)" d="M354,381.2c6.8,3.4,5.4,7.4-5.6,10.4c-10.7,3.1-31.1,5.1-54.4,8.4 c-23.3,3.3-43.7,0.8-54.4-2.4c-11-3.4-12.4-7.6-5.6-13.8c6.8-7,18.9-14.6,29.6-17.4c11-3.3,20.6-1.8,30.4-1.4 c9.8,0.4,19.4,5.1,30.4,8.3C335.1,376.8,347.2,378.6,354,381.2z"/> <path id="blob3" fill="url(#blob3_2_)" d="M312.7,147.3c-2.1,16.4-15.3,27.2-23.2,25.3c-8.1-1.8-12.6-13-14.8-24.9 c-1.9-11.8,2.7-22.7,14.8-25.3C301.5,119.6,314.7,130.8,312.7,147.3z"/> <path id="blob4" fill="url(#blob4_2_)" d="M317.8,147.4c-1,8.2-9.8,10.3-13.8,9.3c-4-0.9-6.5-3-7.6-8.9c-1-5.9,2.3-8.5,8.4-9.8 C310.8,136.6,318.8,139.1,317.8,147.4z"/> </svg>

Notice I've just added some comments in the <defs> between the blocks of gradients to show which ones refer to which.

Your CodePen preview pane should look like this - if it doesn't then SIT IN THE BIN:







Group And Mask Thy Blobs, Kindly Stranger

Remember we put the glassShape into a clipPath ? Well now we'll use that to clip all the blobs so they look like they're inside the lava lamp but first we need to group all of the blobs together. That way we can clip the entire group in one go.

So, staying in CodePen, go and find the path with the id of blob0 and add a <g> tag above it.

Go right down to below the last blob ( blob4 ) and close the tag with </g> . That groups the blobs.

Now go back to the opening <g> and add the clip path attribute like this:

<g clip-path="url(#glassMask)">

This clip path references the <clipPath> tag we wrote earlier in the <defs> .

Your code in the CodePen pane should look like this:

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> <clipPath id="glassMask"> <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/> </clipPath> <!-- LAMP GRADIENTS--> <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174"> <stop offset="1.530612e-002" style="stop-color:#000000"/> <stop offset="0.233" style="stop-color:#050202"/> <stop offset="0.4808" style="stop-color:#120706"/> <stop offset="0.7421" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379"> <stop offset="5.102041e-003" style="stop-color:#000000"/> <stop offset="0.2251" style="stop-color:#050202"/> <stop offset="0.4754" style="stop-color:#120706"/> <stop offset="0.7394" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <!--END LAMP GRADIENTS--> <!-- BLOB GRADIENTS --> <radialGradient id="blob0_2_" cx="292" cy="171.5" r="56.5354" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob1_2_" cx="297" cy="167.5" r="37.2156" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob2_2_" cx="294" cy="157" r="23" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="botBlob_2_" cx="284.5" cy="421.5" r="53.521" gradientTransform="matrix(-2.802637e-002 -0.9996 5.9976 -0.1682 -2235.533 776.7669)" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob3_2_" cx="291.9382" cy="167.4587" r="41.0767" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob4_2_" cx="306.5" cy="155" r="14.109" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <!-- END BLOB GRADIENTS--> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> <polygon id="lampTop" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <path id="lampBot" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5 c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop" y="470" width="600" height="130"/> <g clip-path="url(#glassMask)"> <path id="blob0" fill="url(#blob0_2_)" d="M326.2,149.5c-5,19.2-21.4,29.2-37.8,26.6c-16.5-2.9-33.4-12.9-37.1-26.6 c-3.8-13.6,12.5-32.1,37.8-34.9C314.4,111.8,331.3,130.4,326.2,149.5z"/> <path id="blob1" fill="url(#blob1_2_)" d="M320.5,146.4c-4.4,10.1-16.4,20.2-26.8,25.3c-10.4,5.2-22.4-2.9-26.8-15.2 c-4.4-11.6,7.6-20.4,26.8-25.3C312.9,126.3,324.9,135.6,320.5,146.4z"/> <path id="blob2" fill="url(#blob2_2_)" d="M278,147.7c2.7-7.1,9.4-15.7,15.4-16.4c5.9-0.4,12.6,8.5,15.4,16.9 c2.7,8.4-4.2,14.9-15.4,14.2C282.2,161.5,275.3,154.8,278,147.7z"/> <path id="botBlob" fill="url(#botBlob_2_)" d="M354,381.2c6.8,3.4,5.4,7.4-5.6,10.4c-10.7,3.1-31.1,5.1-54.4,8.4 c-23.3,3.3-43.7,0.8-54.4-2.4c-11-3.4-12.4-7.6-5.6-13.8c6.8-7,18.9-14.6,29.6-17.4c11-3.3,20.6-1.8,30.4-1.4 c9.8,0.4,19.4,5.1,30.4,8.3C335.1,376.8,347.2,378.6,354,381.2z"/> <path id="blob3" fill="url(#blob3_2_)" d="M312.7,147.3c-2.1,16.4-15.3,27.2-23.2,25.3c-8.1-1.8-12.6-13-14.8-24.9 c-1.9-11.8,2.7-22.7,14.8-25.3C301.5,119.6,314.7,130.8,312.7,147.3z"/> <path id="blob4" fill="url(#blob4_2_)" d="M317.8,147.4c-1,8.2-9.8,10.3-13.8,9.3c-4-0.9-6.5-3-7.6-8.9c-1-5.9,2.3-8.5,8.4-9.8 C310.8,136.6,318.8,139.1,317.8,147.4z"/> </g> </svg>

Your CodePen preview pane should now show the clipped blobs like this:







We will be revisiting the SVG code once more after we've added the JS and that will be to add the filter to create the goo effect.

Make My Blobs Move Please, Officer

(These headings are getting weird)

Right now it's time to so some actual proper JavaScript coding. We'll be using the GreenSock (GSAP) TweenMax library not only because it has everything we need, like TimelineMax, but also because it's completely awesome. Simple.

In the JS pane in CodePen, click the little cog icon, and if you're feeling lazy you can add it by selecting it from the Quick Add dropdown. Alas this adds an older version of TweenMax (vs. 1.16.1) which is perfectly fine but I prefer to keep things up to date so I suggest pasting in the latest version below:

https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js

The JS settings pane looks like this:

The Final JavaScript Code In One Small Hit

The code we'll write is mercifully brief - about 20 lines. I'll paste it all here, then break it down, line by line.

function randomBetween(min,max) { return Math.floor(Math.random()*(max-min+1)+min); } var tl = new TimelineMax(); for(var i = 0; i < 5; i++){ var t = TweenMax.to(document.querySelector('#blob' + i), randomBetween(14, 50), { y:260, repeat:-1, repeatDelay:randomBetween(1, 3), yoyo:true, ease:Linear.easeNone }) tl.add(t, (i+1)/0.6) } tl.seek(100);

JavaScript Code Breakdown

function randomBetween(min,max) { return Math.floor(Math.random()*(max-min+1)+min); }

So the first thing we'll write is randomBetween , a function I use all the time. You pass in a minimum and maximum value and it spits back a random value between them. We'll use this to add some randomness to durations and repeat delays.

var tl = new TimelineMax();

Next is the GSAP TimelineMax instance. This will be the timeline to which we will add the individual TweenMax blob tweens.

for(var i = 0; i < 5; i++){ var t = TweenMax.to(document.querySelector('#blob' + i), randomBetween(14, 50), { y:260, repeat:-1, repeatDelay:randomBetween(1, 3), yoyo:true, ease:Linear.easeNone }) tl.add(t, (i+1)/0.6) } tl.seek(100);

Here we loop through the five blobs (excluding the bottom botBlob because that one doesn't move), creating five TweenMax instances, getting a reference to each blob's id using document.querySelector .

y:260 tells each blob to animate to 260 on its Y axis (i.e. down).

This highlights an important point about GSAP and the way it handles the initial X and Y position of SVG paths. As far as GSAP is concerned, when you animate a path from its initial position (i.e. the position it was pasted at from AI) it will treat its X and Y as 0,0 no matter where it actually is on the SVG canvas. So even though the blobs looks like they are around 250 on the X and 120 on the Y, they are considered to be at 0,0. This means that each blob will go 260 pixels down from its original, pasted position.

GSAP treats the X and Y position of a path as 0,0 no matter where it seems to be visually in the SVG canvas.

In that tween we also repeat the up and down animation forever using repeat:-1 and we also control the delay between repeats. This is a another random value repeatDelay:randomBetween(1, 3) . We add in yoyo:true so it goes down, then up, then down, then up, like a yoyo, rather than going down to 260 then jumping back to its original position and going down again.

Finally we use a linear ease (no ease in or ease out) because this just looks more natural.

Still inside the loop we add that TweenMax tween instance ( t ) to the main TimelineMax instance ( tl ), with each instance being added an equal distance in time apart along the timeline.

The timeline is now infinitely long and will repeat forever. If you started it from the beginning of the timeline it would take a while for each blob to start moving because the blobs have not been told to start animating all at the same time. Their start times are staggered.

So to finish off we tell the timeline to jump to around 100 seconds in just to make sure all the blobs are moving like this:

tl.seek(100);

And that's all you need. Your pen should look like this (hopefully):

Bring Me Goo, M'am, And Bring It To Me Now

When you make LEGO vehicles the instructions always leave the wheels until last. I've sort of stolen that idea, just swap 'wheels' for 'goo'.

In the CodePen HTML pane paste the following <filter> tag into the <defs> tag (anywhere will do).

<filter id="goo"> <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" /> <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 21 -9" result="cm" /> </filter>

Nothing will happen until we apply it to the group of blobs. Go and find the <g> tag that groups the blobs and add the following as an attribute.

filter="url(#goo)"

So your tag now looks like this:

<g clip-path="url(#glassMask)" filter="url(#goo)" >

Your lava lamp should now be blobbing around nicely.

The Final SVG Code In One Big Fat Hit

<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve"> <defs> <filter id="goo"> <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" /> <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 21 -9" result="cm" /> </filter> <radialGradient id="bgGrad" cx="300" cy="300" r="300" gradientUnits="userSpaceOnUse"> <stop offset="7.142857e-002" style="stop-color:#471A19"/> <stop offset="0.3107" style="stop-color:#290F0E"/> <stop offset="0.553" style="stop-color:#120706"/> <stop offset="0.7828" style="stop-color:#050202"/> <stop offset="0.9847" style="stop-color:#000000"/> </radialGradient> <clipPath id="glassMask"> <path id="glassShape" d="M262,174h60l33.5,182.3c0,0,2.7,12.8,2.5,22.8c-7.5,0-131,0-131,0s-0.7-9.3,0-18 C227.6,352.9,262,174,262,174z"/> </clipPath> <!-- LAMP GRADIENTS--> <linearGradient id="lampTop_2_" gradientUnits="userSpaceOnUse" x1="292" y1="135" x2="292" y2="174"> <stop offset="1.530612e-002" style="stop-color:#000000"/> <stop offset="0.233" style="stop-color:#050202"/> <stop offset="0.4808" style="stop-color:#120706"/> <stop offset="0.7421" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <linearGradient id="lampBot_2_" gradientUnits="userSpaceOnUse" x1="292.375" y1="470" x2="292.375" y2="379"> <stop offset="5.102041e-003" style="stop-color:#000000"/> <stop offset="0.2251" style="stop-color:#050202"/> <stop offset="0.4754" style="stop-color:#120706"/> <stop offset="0.7394" style="stop-color:#290F0E"/> <stop offset="1" style="stop-color:#471A19"/> </linearGradient> <!--END LAMP GRADIENTS--> <!-- BLOB GRADIENTS --> <radialGradient id="blob0_2_" cx="292" cy="171.5" r="56.5354" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob1_2_" cx="297" cy="167.5" r="37.2156" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob2_2_" cx="294" cy="157" r="23" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="botBlob_2_" cx="284.5" cy="421.5" r="53.521" gradientTransform="matrix(-2.802637e-002 -0.9996 5.9976 -0.1682 -2235.533 776.7669)" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob3_2_" cx="291.9382" cy="167.4587" r="41.0767" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <radialGradient id="blob4_2_" cx="306.5" cy="155" r="14.109" gradientUnits="userSpaceOnUse"> <stop offset="1.020408e-002" style="stop-color:#FF9C12"/> <stop offset="0.1922" style="stop-color:#FA9712"/> <stop offset="0.3992" style="stop-color:#ED8A14"/> <stop offset="0.6186" style="stop-color:#D67316"/> <stop offset="0.8449" style="stop-color:#B65419"/> <stop offset="1" style="stop-color:#9C3A1C"/> </radialGradient> <!-- END BLOB GRADIENTS--> </defs> <rect id="bg" fill="url(#bgGrad)" width="600" height="600"/> <use xlink:href="#glassShape" fill="#EB7619" opacity="0.1"/> <polygon id="lampTop" fill="url(#lampTop_2_)" points="269,135 262,174 322,174 316,135 "/> <path id="lampBot" fill="url(#lampBot_2_)" d="M226.8,379c2.6,43,23.9,54.6,28.3,60.2c3.3,5.4-10,30.8-10,30.8h95.5 c0,0-16.5-25.1-14.5-30.8s26-15.2,32-60.2C328,379,240.3,379,226.8,379z"/> <rect id="tableTop" y="470" width="600" height="130"/> <g clip-path="url(#glassMask)" filter="url(#goo)" > <path id="blob0" fill="url(#blob0_2_)" d="M326.2,149.5c-5,19.2-21.4,29.2-37.8,26.6c-16.5-2.9-33.4-12.9-37.1-26.6 c-3.8-13.6,12.5-32.1,37.8-34.9C314.4,111.8,331.3,130.4,326.2,149.5z"/> <path id="blob1" fill="url(#blob1_2_)" d="M320.5,146.4c-4.4,10.1-16.4,20.2-26.8,25.3c-10.4,5.2-22.4-2.9-26.8-15.2 c-4.4-11.6,7.6-20.4,26.8-25.3C312.9,126.3,324.9,135.6,320.5,146.4z"/> <path id="blob2" fill="url(#blob2_2_)" d="M278,147.7c2.7-7.1,9.4-15.7,15.4-16.4c5.9-0.4,12.6,8.5,15.4,16.9 c2.7,8.4-4.2,14.9-15.4,14.2C282.2,161.5,275.3,154.8,278,147.7z"/> <path id="botBlob" fill="url(#botBlob_2_)" d="M354,381.2c6.8,3.4,5.4,7.4-5.6,10.4c-10.7,3.1-31.1,5.1-54.4,8.4 c-23.3,3.3-43.7,0.8-54.4-2.4c-11-3.4-12.4-7.6-5.6-13.8c6.8-7,18.9-14.6,29.6-17.4c11-3.3,20.6-1.8,30.4-1.4 c9.8,0.4,19.4,5.1,30.4,8.3C335.1,376.8,347.2,378.6,354,381.2z"/> <path id="blob3" fill="url(#blob3_2_)" d="M312.7,147.3c-2.1,16.4-15.3,27.2-23.2,25.3c-8.1-1.8-12.6-13-14.8-24.9 c-1.9-11.8,2.7-22.7,14.8-25.3C301.5,119.6,314.7,130.8,312.7,147.3z"/> <path id="blob4" fill="url(#blob4_2_)" d="M317.8,147.4c-1,8.2-9.8,10.3-13.8,9.3c-4-0.9-6.5-3-7.6-8.9c-1-5.9,2.3-8.5,8.4-9.8 C310.8,136.6,318.8,139.1,317.8,147.4z"/> </g> </svg>





I went through this tutorial myself and built the pen below following it (it's a slightly simplified version of the my original version but it looks exactly the same).

For the purposes of making these pens looks better in this post I've added in a bit of CSS to the body to stop any scrollbars.

body { background-color:#000; overflow: hidden; }

As a little extra, if you're not completely happy with the timing of the blobs, try adding in a TimelineMax.timeScale(); call after the seek(); line like this:

tl.timeScale(12);

This will make the blobs go 12 times their normal speed for crazy-speed-blob-action!

So there you have it - we've covered quite a fair bit of stuff, from Illustrator tips and tricks, SVG filters and grouping to reusing elements using <use> and changing the timing of the entire GSAP animation. And thankfully the code is pretty minimal due to GSAP being so damn powerful.

You can download the Adobe Illustrator file for the lava lamp here.

Now go and relax in the sumptuous glow of your new lava lamp - you've earned it!

Related posts:

Seven Stages of SVG

My First SVG Banner Ad