Monday, May 16, 2016

CSS3 Smoke Animation Effect

The comic today uses quite a bit of CSS3 animation. This is a rather new thing for me - I've been using mostly JavaScript-powered animations on since it started. But after I did the rain animation using CSS back in March (see the comic here), I've been warming up to the idea of more CSS and less JavaScript to move things around.

For this task, I wanted to make some animated smoke come out of the frogs' rocket ship before the launch. My style is mostly just simple geometric shapes arranged to look like things, so using circular DIV elements to look like puffs of smoke was fine with me. I found a great starting point by Andrea Verlicchi on CodePen, and then modified it for my comic.

Here's the basic idea - puffs of smoke emanate from a given source element. They move downward and off to the side while fading away.

The puffs of smoke will be represented by rounded SPAN elements, with this CSS applied to them:

span.smokepuff {
    display: block;
    position: absolute;
    bottom: -35px;
    left: 50%;
    margin-left: -20px;
    height: 0px;
    width: 0px;
    border: 35px solid #4b4b4b;
    border-radius: 35px;
    left: -14px;
    opacity: 0;
    transform: scale(0.2);

The above styling just makes them round, grey, and positioned absolutely in their container. I also have two animation keyframes defined, one for the down-and-left movement and one for the down-and-right movement:

@keyframes smokeL {
    0% {
        transform: scale(0.2) translate(0, 0);
    10% {
        opacity: 1;
        transform: scale(0.2) translate(0, 5px);
    100% {
        opacity: 0;
        transform: scale(1) translate(-50px, 80px);

@keyframes smokeR {
    0% {
        transform: scale(0.2) translate(0, 0);
    10% {
        opacity: 1;
        transform: scale(0.2) translate(0, 5px);
    100% {
        opacity: 0;
        transform: scale(1) translate(50px, 80px);

The above keyframe definitions define an animation that will move the smoke puffs lower by 80 pixels and 50 pixels to either side while at the same time scaling them up and fading them out. It defines 3 steps: 0% (the start), 10% (moved a little down), and 100% (moved completely down and over). There's one for the left, smokeL, and one for the right, smokeR.

Note: if you care about being compatible with slightly older browsers, you would want copies of these with @-moz-keyframes and @-webkit-keyframes as the names as well as adding -moz-transform and -webkit-transform to them all! I left that out here to keep the example simpler!

I said this was pure CSS3 animation, but there's still a little JavaScript involved. It doesn't really do the animating, but I use some code to generate the puffs in the first place. Something like this:

function createSmoke(time, num) {

    var timeGap = (time / num); 

    for( var i = 0; i < num; i++) {

        var delay = (timeGap * i) + 's';

        var aniName = "smokeL";
        if (((i+1) % 2) == 0) {
            aniName = "smokeR";

        var aniStyle = "animation: " + aniName + " " + duration + " " + delay + " infinite";
        $('#smoker').append('<span class="smokepuff" style="' + aniStyle + '"></span>');



When this function is called, you give it the length of the animation and the number of puffs of smoke you want. It figures out how much of a delay there should be between each puff's animation starting based on those two values. For example, if you want the animation to run 5 seconds and have 10 puffs of smoke, the first puff would have no delay, the second would have a delay of ( 5 / 10 ) * 1, the third a delay of (5 / 10 ) * 2, and so on. In this example, that just means add a half-second delay for each puff you generate. Also, each time through the loop, it alternates between the smokeL and smokeR animations so that every other puff moves in the opposite direction. One final piece of the total animation style is to set the repeat-count to infinite, so the puffs keep on coming! The function generates new SPAN tags with these animation styles applied and appends them to the parent element, which here is named smoker. It's just a DIV somewhere on the page - all the puffs of smoke will appear to come out of it.

The finished animation. Don't miss it!

I think the effect turned out great. Even though I used a little JavaScript to create the elements, doing all the animation with JavaScript would have been much, much more complicated. I may consider replacing all the old JavaScript-powered animation on with CSS animation, and fix up my animation editor to go with it!

Now don't miss the smoke effect in today's comic! If you're reading this on the publication day (16 May 2016) the countdown to launch will be live! That means the last frame of the comic will change and do different things right up to the launch time! Keep watching it! comic for 16 May 2016

Monday, May 9, 2016

Add Gamepad Support to a Phaser Game

It seems like I haven't written a blog post in forever! It's actually been less than 2 weeks. Right before I took a break, I wrote a little about my new NES-style Bluetooth gamepad. I've been trying off-and-on ever since then to get my 8-bit style platformer to work with it, and today I finally had some success!

First of all, it needs to be said that support for gamepads in the browser is very inconsistent. The W3C's Gamepad API document is still a working draft after all these years (I first read about it and tried it out in 2013). It seems as though Mozilla and Google have some different opinions on how it should work, because the way you interact with the devices varies significantly between Firefox and Chrome. Phaser provides gamepad support through the Gamepad object, but the documentation carries a warning about the volatility of the specification.

Here's what I learned when I tried to use it...

I started with some of the examples on Phaser's site. They worked, most of the time. Let me explain. In theory, working with a gamepad in Phaser is simple. You get a gamepad object, setup a callback to handle the detection of a gamepad device, and bind to buttons in that callback. Then you start the gamepad polling.

function create() {

    // ... setup stuff ...

    var jumpButton = null;

    controller = game.input.gamepad.pad1;

    controller.addCallbacks(this, {
        onConnect: function() {
            // you could use a different button here if you want...
            jumpButton = controller.getButton(Phaser.Gamepad.BUTTON_1);


    // ... other stuff ...


function update() {

    // ... other stuff ...

    if (jumpButton.isDown) {
        // jump code goes here!

    // ... other stuff ...


Much like you do for keyboard input, you can set up the buttons you want to listen for in create and then perform actions based on their state in update. And this works pretty well - in Firefox. Chrome, on the other hand, has some issues. Phaser's example code, much like my example above, works most of the time in Chrome when the code gets executed very quickly after the page loads. But if you put enough setup code in front of your gamepad initialization you'll be wondering, like I was, why your gamepad never connects.

I had to dig into the Phaser code in order to figure this out. It works consistently in Firefox because Firefox waits until the first time a button is pressed on a gamepad before it emits a gamepadconnected event from the window object. Phaser catches that and sets everything up, calling the onConnect function when complete. In Chrome, however, gamepads just show up magically at some point after the page is loaded, in an array-like object accessed by calling navigator.getGamepads(). Phaser checks this list constantly, and when things appear for the first time, it makes all the internal setup calls. And right there's the problem! If the gamepads appear BEFORE my onConnect callback function is set up, I missed the boat. A default, no-op callback got executed instead and my gamepad buttons never get set up!

There was no work-around for this that I felt was acceptable, so I actually forked Phaser and fixed the problem in the Gamepad object's code. It was a fairly simple fix - I just don't start polling for those gamepad objects until after the call has been made to game.input.gamepad.start().

I forked off of version 2.4.7 and submitted a pull request, so hopefully my fix makes it in to the next Phaser release and the rest of you won't have to deal with this problem like I had to! If you can't wait, try using my fork and gamepad branch.
UPDATE: My pull request was merged, but not in time for 2.4.8. Look for this fix in the 2.4.9 release!
If you're just interesting in playing the game I've been working on, you can do that here: The full source code is available on GitHub. If you're just interested in viewing today's comic, you can do that here: comic for 9 May 2016

Wednesday, April 27, 2016

The Most Awesome Thing on the Internet

Here it is, people. The most awesome thing on the Internet (besides my frog comics) - a
Mariachi Cover of the Dark World theme from The Legend of Zelda: A Link to the Past.

You can thank me later (by viewing and sharing today's comic!). Oh look, here it is... comic for 27 April 2016

Monday, April 25, 2016

Review of Xgaming's NES-style Wireless Gamepad

Late last week I received my new Bluetooth wireless gamepad from Xgaming. I was really excited about this new product. I've had one of their arcade joysticks for years and found it to be an excellent piece of hardware that fully lives up to the high expectations set for it on their web site. I was certain that this new wireless gamepad, a bit of a departure from their existing product line, would not disappoint me.

So, what do I think now that I've had a few days to play with it?

Let's start with my initial impressions. It was a little smaller than I had expected it to be. It doesn't really give dimensions anywhere on Xgaming's site (as far as I could tell) but the unit is only 5.125 inches wide and 2.5 inches tall. It's slightly over half an inch thick (not counting the sticks and buttons). For me, the small size makes hitting both sets of top trigger buttons awkward but maybe I just need to get used to it. Most games I play only need one set of top triggers anyway. Besides that, the position of all the buttons and sticks is excellent. It weighs only 3 ounces, but feels very solid. The buttons and D-pad are very firm and have a wonderful feel to them.

Technically, it has been functioning very well. I had no problems pairing it to my Windows 10 PC as a Bluetooth game controller, but it did seem to lose its pairing once (there are colored LED lights on the bottom which indicate things like that - flashing blue means ready to pair). I'm not sure if it was the device's fault or mine - I may have been holding down one of the settings buttons inadvertently while powering it on. I'll withhold judgement on that issue until I see if it repeats. There were no issues setting up button mappings in my emulator software either. It all worked very well.

Playing games with it has been very enjoyable so far. The button response is much better than my old SNES-style USB game controller. I honestly haven't liked a PC game controller this much since my Gavis PC GamePad.

The device is currently selling for about $45. If you love playing emulated NES and SNES games as much as I do, I believe you'll find it to be well-worth the money.

The only question left might be, does it work in web-based HTML5 games such as the 8-bit style platformer I've been working on? The answer is...maybe. Web browser gamepad support is still weird. Phaser has an API for it, but so far I've found it difficult to work with. I'll keep trying and maybe have something to report next week.

Until then, I've got more comics for you to enjoy! comic for 25 April 2015

Friday, April 22, 2016

Answering Questions

Tonight I'm working on answering interview questions. Yes, I'm being interviewed by Best WebComics, a site that promotes the discovery of new webcomics. Their questions are rather unique so the finished interview should be a good read. I also get promoted on their site for a week next month.

I've been working on the comics for over 2 years now. The first comic was published in August of 2014 but I actually started working on it in January of that year. It took almost 8 months to get things ready. I had to develop the web page stuff, the back-end application that runs everything, make the editor for the comics, and write a bunch of comics. Looking back on some of my early work, I think I've gotten a lot better.

So when you view today's comic, why don't you click the link to go back to the first comic too? Even if you've been reading them from the beginning, you've probably forgotten the early ones. comic for 22 April 2016

Wednesday, April 20, 2016

Read Another Blog Today

As I said last week, I'm not spending as much time writing this blog as I normally have been. I need a few weeks off from it due to the demands of the real world. But that doesn't mean that you, the reader, have to suffer. Please take the time you would have normally spent reading my inane ramblings to read someone else's.

Here are some excellent options:

Smoke in the Cinderhaze
I'm not exactly sure what a "Cinderhaze" is, but I suspect that it's a word used to describe Cinderella's mental state before she has her first cup of coffee in the morning. Either that or some kind of sailor thing. This tech blog is written by my friend Daryl Wiest, and he doesn't update it often enough. Go leave him some comments about that.
David Walsh Blog
An excellent tech blog that covers a variety of topics, but often covers JavaScript and front-end stuff. Also some excellent career advice sometimes. This David Walsh guy is fairly famous so you might have heard of him already. If not, you should.

While you wander off to read those, don't forget to read today's comic! It's #314, which is a lower number than Monday's comic (#315). That kind of thing hasn't happened in a while. I move the publish dates around from time to time after I make them. I hope it doesn't bother anyone. comic for 20 April 2016

Monday, April 18, 2016

Better Static Images for

The new, improved version of the image for a recent comic.
As I mentioned last week, I have been working to close Issue #7 on my webcomic. Basically, is not a normal webcomic. It's not an image, which is great until someone wants to share a link to a comic on social media. I auto-generate static images of the comic just for sharing. But the comics which contain embedded games and stuff haven't looked right in the static captures. I wanted to at least put a warning in the image that explains why they often look weird, but as an added bonus I actually made them look better (most of the time).

As you can see in the picture on the right, I was able to add some warning text to the top of dynamic comics. I generally share this version on but you may see it other places as well. When people see comics like this in the future, they'll know that they should view it on to get the full effect.

Also, when this comic was originally published, the third frame was black. The game was not captured as part of the image. This was due to two things - the URL of the page used for the image capture was such that the game's image assets could not be loaded, and the fact that the capture was taken before the game initialized asynchronously. I fixed the URL issue by changing the static image comic path from /basic/[comic-number] to /[comic-number]&b=1. There wasn't really a generic way to ensure the comic will be rendered before the capture in all cases, but I added a 500 millisecond delay which should be good enough the majority of the time.

Finally, I also locked-down the ability to see the basic versions of the comic. It was the case that you could view a future comic if you asked for the basic rendering. I changed the rules so that only authenticated users or requests coming from the server itself can see them. I use PhantomJS to perform the image capture and it runs on the server, so that was a simpler fix than I initially thought that it would be.

It always feels good to resolve an issue. It also feels good to read one of my comics. Here's the link so you can feel good too. comic for 18 April 2016

Friday, April 15, 2016

Basic Images are Complicated

I sat down tonight thinking that I would quickly resolve Comic Issue #7, which has to do with the static images generated for sharing on social media platforms. Here it is almost midnight and I am not finished.

It's been bothering me that many of the dynamic comics (the ones that use JavaScript for a game or something) are not rendering correctly at all as static images. See this post on how I generate the static images. I finally figured out that it is because I take pictures of "basic" versions of the comics (no header, footer, etc.) and I generated the "basic" versions of the comics at a slightly different URL. When the page rendered there, sometimes file paths used in embedded JavaScript would be wrong and lead to missing resources which in turn leads to a bad picture.

Another problem with this method was that I render the images before the comic's official release date and so I had to leave the basic URL open - that is, no authentication was required to see future comics if you requested them in basic form. I doubt that many people took advantage of this flaw, but still...

Of course I wanted to fix all these problems in addition to just marking dynamic comics as dynamic in the basic view. So I'm not finished with any of it. I'm like, 85% finished. But tomorrow is another day. A day in which I might write another post about what I actually did to fix the problems! Until then, entertain yourself with today's comic. It's funny, and that's no accident. comic for 15 April 2016

Wednesday, April 13, 2016

Use Tiled's Map Background Color in Phaser

I actually did some work on my Phaser platformer this week! Yeah! I resolved an issue that was submitted on GitHub concerning the water physics in Level 7, and I started thinking some more about another thing which had been bothering me.

Even though I can set a map's background color in the Tiled map editor, Phaser doesn't parse that property when it builds a Tilemap object. I had been keeping track of the background colors for each level separately, but that violates one of my principles - the map editor should be the one and only source of data about each level map.

The Background Color map property in Tiled

I was able to make a few minor changes to work around this limitation. In the stage's create function, I first pull the tilemap data manually for the map's key. This is the same data that is used behind-the-scenes when you call game.add.tilemap(key).

function create() {

    // get the asset key for this level
    mapKey = "key-for-this-level";

    // pull the tilemap data from the asset cache
    var tilemapData = game.cache.getTilemapData(mapKey);

    // all the data from Tiled is in the data field
    var mapData =;

    // set the stage's background color from the map's background color.
    // note that Tiled didn't capitalize the "c"...
    game.stage.backgroundColor = mapData.backgroundcolor;

    // go about making the make the normal way
    var map = game.add.tilemap(mapKey);
    map.addTilesetImage("ground", "tiles");

    // ... the rest of the stuff ...


The tilemap data object returned from the asset cache has itself a data field, which is essentially the Tiled JSON map object. The backgroundcolor property is there can can be used to set the backgroundColor property for the stage. Remember, you can view the full source code to this game in its public repo on GitHub!

It really was that simple. In only a few minutes, I feel like I accomplished something on this game for the first time in a month! It doesn't take much to make me feel good about my achievements. Now, go read today's comic and feel good about that achievement! comic for 13 April 2016

Monday, April 11, 2016

A Little Break

Over the last month, regular readers here might notice that the stresses of travel and a new job have greatly limited my ability to do interesting things and blog about them. It's just been too hard to keep up both the writing of the blog and the writing of the comic. I've decided that the comic is more important and must continue on its regular publication schedule, while blog posts might get a little more irregular.

I haven't forgotten about my 8-bit-style Phaser platformer, and I will be finishing it up over the next few weeks - I just might have one blog post per week instead of three.

I will try very hard to get back on my old schedule as soon as possible. And speaking of things being back on schedule, look what's on Cartoon Network's schedule: a reboot of the Powerpuff Girls! Along with Dexter's Laboratory, the Powerpuffs were one of my favorite shows back when I was in college. My wife, who was known to watch them with me back in those days, even has the Heroes and Villians Powerpuff Girls soundtrack CD somewhere in this house. We added it to our iTunes library years ago but no longer have any iDevices so I haven't heard it in a while...

Anyway, to promote the new series Cartoon Network put together this Powerpuff Yourself thing and I used it to make this Powerpuff version of my wife:

Powerpuff Rebecca, with her super-powered latte.

It looks just like her, trust me.

And you might say that the art style of the Powerpuff Girls influenced my own work. You might be right. I loved the art in the original series. The new one is very similar, but it's not quite the same. Or maybe it is and I've just gotten older. comic for 11 April 2016

Friday, April 8, 2016

New Things to Learn

I'm back home in Pennsylvania after spending 4 days in Virginia learning about my new job. I'll be working with some technologies that I haven't really used much before, so maybe I'll have some new stuff to share here soon. Angular is currently at the top of my list of things to learn better.

So here's another super-short blog post followed by a link to another super-entertaining comic. Well, at least mildly-entertaining. comic for 8 April 2015

Wednesday, April 6, 2016

I'm Tired of Hotels

So here I am in another hotel. In the past 5 weeks, I've been to Texas, Florida, and Virginia. I don't mind a business trip every now and then, but this has been difficult. This week I'm in Virginia because I'm starting a new job and need to learn some things before I go back home to Pennsylvania. So far I've learned that I am really tired of hotels.

They've all been nice hotels. There's nothing really wrong with them. There's just something wrong with me. A lot of people travel much more than I do, and I am starting to think those people are crazy. Back six months ago, I was able to do some work on my comic and some games while in a hotel room. Now I can't even get Eclipse to start. I once wrote 2 blog posts per night in my hotel. Now I forget that I even have a blog until the last minute. I may have to take a hiatus from the blog writing for a while so that I don't get behind on the comic too. I haven't decided yet.

My friend Daryl tries to help keep me working on things by writing issues on GitHub for my unfinished games. Maybe if more people did that I would be forced to work on them more. Why don't you go read my comics and play some of my games right now and write an issue for a bug fix or improvement on one of them? That would really help me out. It would help more if you did the fix yourself and just sent the fix... comic for 6 April 2016

Monday, April 4, 2016

New Job Today

The main reason that I've been under too much stress to work on anything lately is because I'm starting a new job today. Yes, after nearly 15 years at the same one, I recently resigned. I've worked for the same company since I graduated from college, so this is a very new experience for me. Lots of stuff is changing. And I hate change.

So once again, I have nothing to share in this blog. I'm sorry. Hopefully in no more than a few weeks I'll be settled in to a routine and can go back to gleefully making frog games in my spare time. But for today, all I have is today's comic. It's the first of a short series. comic for 4 April 2016

Friday, April 1, 2016

April Fools!

Things are still out of control here. I've had no more time to work on that Science Frog game this week, and April Fools Day caught me off-guard. Making a comic for today proved more time-consuming that it should have been, and in the middle of it all I got sucked into a Google code challenge thing that ended up not even working! Google's fault, not mine (pretty sure).

What? Yeah, so it turns out that Google does some recruiting by looking at the kind of things for which you search. I don't claim to know their algorithm for identifying potential candidates, but if you are searching for computer science terms you might get an invitation to play a game in your search results. It happened to me Tuesday night, and despite the obvious parallels to War Games and the fact that I was really tired when it happened, I agreed to play the game.

I was given a programming challenge and a Unix-like terminal-in-a-web-page in which to write some code and test it before submission. I wrote up a solution within a few minutes, but got a very unhelpful error message when I attempted to verify it against the test cases. I was so certain that my solution was correct, but I was too tired to look at it any more. I left the window up and came back to it the next day after work.

When I sat down, the first thing I did was just try to verify again. This time, even though I hadn't changed anything, I got a valid response. I was surprised so I verified again. This time I got an error response: 400 bad request. I tried again. That time it worked. Obviously, something was going wrong intermittently on the Google side of the test. Knowing that my solution was most likely correct, I perhaps foolishly submitted it. Did the submit work? No, 400, bad request. I hit submit again. Same thing. I tried verify again. No. And again. No. It never worked again after that. Oh well, it might have been fun to continue with it...but I had to eventually close the window.

I'm not even kidding. This is no April Fool's joke. It really happened.

Know what else happened? I wrote this comic. No picture link today. It just wasn't working. Just like last year's April Fools comic, these "special" ones break my automated image capture.

Wednesday, March 30, 2016

Dependency Cost

Over the weekend I was alerted to a bug in the web application by a reader. For him, the "previous" and "next" navigation links would not work. Clicking on them put the page into endless "Grabbing Frogs" mode and never displayed another comic.

Fortunately, the issue was not difficult to correct. Since is (almost) a single-page-application, each time a new comic replaces the currently visible one I have to generate a new Pinterest button for the correct comic. Yeah, you're probably wondering, "why a Pinterest button?" I don't know. It seemed like a good idea at the time...

I'm generating the new button by calling a slightly undocumented function in the Pinterest code. I don't think they intended for it to be used on pages quite like mine. The problem arises when the function is unavailable. Maybe Pinterest is blocked. Maybe Pinterest is down. Maybe certain browsers actually download an older version of the code where that function doesn't exist. Doesn't matter why - the end result is the same. Without that function, an error occurs in my JavaScript and the new comic is never displayed. I fixed this issue and then immediately found another similar issue with my call to Emoji One's JavaScript. Fixed that too.

This got me thinking about what I'll call Dependency Cost. I didn't really think about the cost of my code being dependent on Pinterest and Emoji One behaving in a certain way. I don't control either of them, and they could change or vanish at any time - resulting in my comic readers getting shut out of my site. When you think about it, this is really an unacceptable situation. But how many situations like this are you in right now with your code? I think we're all in too many. Take the setup for example - it's built on Node using Express and over a dozen other modules all downloaded from npm at first install. What if I had to recreate the site and npm was down. Do I think npm will be around forever? Nothing lasts forever.

Just something to think about when you're building your next application. Or you can think about today's comic. That's much more fun. comic for 30 March 2016

Monday, March 28, 2016

Know Your vi

I through in a little jab at vi in the third frame of today's comic. The truth is, I actually use vi almost every day at work. Remote displays are such a hassle. It got me thinking about this nice vi cheat sheet I was given on my first day of work (almost 15 years ago).

It hung on the wall of my cubicle all these years. I found a copy of it on the Internet and wanted to share it with you!

There are a couple of cleaned-up versions of this same style sheet floating around. I like it because it spells out "vi" with the commands. Just in case you forget what the commands are for. Mine is a copy of a copy of a copy and has some hand-written notes on it too, just like the one I found above. Gives it character.

Right after you read today's comic, go log on to a Linux terminal and edit something with vi! comic for 28 March 2016

Friday, March 25, 2016

Easter Eggs

While today's comic is about Easter eggs (maybe?) the comic itself does not contain any. And by that I mean it doesn't have any fun undocumented features. It might have real eggs. Who knows?

The term "Easter Egg" for an undocumented message, joke, or feature in a software product comes from the concept of the Easter Egg Hunt where children search for brightly colored eggs containing prizes. Similarly, if you find the hidden commands or whatever to unlock the Easter Egg in a software application, you get the prize. The hiding of Easter Eggs inside software dates back an Atari game from the late 1979 when the uncredited developer hid his name inside the game. It was supposedly given the name "Easter Egg" by Atari personnel after its discovery.

If I'd had more time, I would have put one in the comic today. But, alas, my life is a bit of a disaster lately and I'm not even sure what day it is most of the time. I just got back from my second business trip in 3 weeks and I have a list of over 10,000 things to do before the end of the month. Okay, I'm exaggerating a little. The list is really only 9,875 things.

The closest thing that has to an Easter Egg is the teapot response. If you go to you will get a message indicating that your tea is ready. But if you look at the HTTP response code, it's actually in the error range - specifically a 418. That's the response to indicate "I'm a teapot" in the Hyper Text Coffee Pot Protocol. I may expand on this someday.

It's not quite an Easter Egg, but there's also a way on the site to force an error condition for test purposes. It lets you see a 2-frame comic which is my lame attempt to tell you that there's been a server-side failure. You will hopefully never see it except by going to

So enjoy today's comic about eggs and Easter but not really Easter but definitely eggs. Yeah. comic for 25 March 2016

Wednesday, March 23, 2016

Perspective Transformations in Inkscape

I still haven't gotten a chance to go back to work on one of my games. I'm on another trip for work this week. But I did learn something new related to today's comic.

While it doesn't have any special features, it does have a piece of paper on the ground with writing on it. The writing is skewed to show perspective. This is something that I hadn't been able to figure out before today - how to make a perspective transformation in Inkscape.

Here's how to do it. First, get whatever object you want to transform. It can be text or a picture of something, but you have to turn it into a path first. You can do that by using the Path - Object to Path menu option.

In this example, I'll transform one of my frog images. It is made up of lots of paths, all in a group. That's ok. To perform the skew, draw another path around the object in the shape that you want it to look like in the end. Start in the lower left corner and draw the line clockwise.

After you have the shape outlined by the new path, select the shape to be transformed first and the path second (Shift+click). Then use the menu option Extensions - Modify Path - Perspective. It takes a few seconds, but should look like this after it runs.
You can delete the outline path if you want.
That's how I made the text on the "landing page" look like it does. I'm always happy when I learn something new. I'm also happy when you read my frog comics. comic for 23 March 2016

Monday, March 21, 2016's got Web Sockets

Today's comic might look like a simple joke about planting light bulbs, but it is really so much more. Well, actually it's only a little bit more. But still more.

The lamp post in the third frame can be turned on and off by clicking on it. But it doesn't just go on and off for you the clicker; it goes on and off for everyone looking at the comic. Try it. Go to and look at the lamp. Call a friend on the other side of the world and have them also go to and look at the lamp. Click on the lamp. You'll both see it toggle state at the same time.

I finally got around to integrating Web Sockets via into the site. The state of the lamp is stored on the server and clicks emit "lamp-toggle" events from the clients. When a toggle event is received on the server, the state of the lamp switches and the new state (on or off) is broadcast out to all the clients. The clients show or hide the "glow" effect accordingly.

Server Code:

var lampOn = true;

io.on("connection", function (socket) {

    socket.on("lamp-toggle", function(data) {

        if (lampOn) {
        } else {

        lampOn = !lampOn;



Client Code:

var socket = io("");

socket.on("lamp-off", function(d) { 

socket.on("lamp-on", function(d) {

$("#lamp").click(function() {

On the server side, I had a little trouble with the fact that I have Node HTTP servers for both secure and insecure web traffic in the application. I wanted the same instance to service both. Fortunately, it is easy to attach a server to multiple HTTP servers. In the code snippet below, I call attach on an existing instance to bind it to a second HTTP server (line 17).

var app = express();

var server = http.createServer(app).listen(3000, function() {
    console.log('listening on port %d', server.address().port);

// create a server attached to the HTTP server just created
var io = require('')(server);

if (ssl) {

    var secureServer = https.createServer(sslOptions, app).listen(4443, function() {
        console.log('listening securely on port %d', secureServer.address().port);

    // attach the server to the secure HTTP server as well


Back in the client, I just had to be smart about which URL to connect to. I never got Web Sockets to work correctly with Nginx, so I'm just bypassing it when I make the connections back from the client. Nginx listens on ports 80 and 443 and proxies HTTP traffic to the Node application which actually listens on ports 3000 and 4443. When I set up in the client, I use these ports directly. Browsers still allow Web Socket connections to different ports on the same server without raising cross-site scripting concerns. But you can't mix insecure HTTP and secure Web Sockets (or vice versa). Look at this update to the client code from above, where I examine the browser's location to determine what URL I should use for connecting:

var urlParts = window.location.href.split("/");
var cUrl = urlParts[0] + "//" + urlParts[2];
if (!window.location.port) {
    if (urlParts[0] !== "https:") {
        cUrl += ':3000';
    } else {
        cUrl += ':4443';

var socket = io(cUrl);

// ... rest of client code from above ...

So go play around with that lamp! And feel free to read more comics while you're there. comic for 21 March 2016

Friday, March 18, 2016


You may have heard stories of brilliant software engineers jotting down code on bar napkins when they have a sudden epiphany while drinking with their friends. Maybe you've done this sort of thing yourself. Maybe, like the frogs in today's comic, you've written down code in a stranger place.

I've never written code with letter-shaped pasta, but I have written out lots of algorithms on paper beside my keyboard. It's never in perfect form for compilation, but is usually just pseudocode. I am not a genius when it comes to algorithms; I can rarely go straight from my brain to they keyboard and have the code work on the first (or fifty-first) try. When I was working on the match-3 game back in January, it took lots of scratch paper pseudocode to get things working correctly.

For whatever reason, writing out the algorithm in pencil and then working through it on paper really works for me. I usually write a list of the variables off to the side, along with their current values as I work through the steps. I erase and re-write the value of each variable as it changes. This is a skill I was taught in college, using a whiteboard instead of paper, but it works just as well.

If you haven't done something similar yourself, give it a try next time you're stumped on an algorithm problem. Or just take a break from programming and read some of my comics! comic for 18 March 2016

Wednesday, March 16, 2016

I Don't Like Gingerbread

I have noticed that it's been over 2 weeks since I worked on my platformer. I haven't abandoned it, but I have taken a break from it to work on other things. A trip to Texas where I got sick and this stupid Daylight Savings Time thing messing up my sleep schedule haven't helped.

This basically means that I don't have any code or Phaser insights to share at the moment. I honestly don't have much of anything to share, other than comics and the fact that I don't like gingerbread.

But today's comic has a gingerbread house! Well, technically it's a gingerbread grocery store, but the fact remains that I don't like gingerbread. When we make "gingerbread" houses around here at Christmastime, we use graham crackers which I consider to be more edible.

Why am I writing about this? I have no idea. comic for 16 March 2016

Monday, March 14, 2016

Tags are Live, and a New Server

After getting home from Texas Saturday evening and despite still having a bit of a cold, I managed to get all the code merged and deployed for the comic tags. New and recent comics will have a set of tags displayed below them, listing the characters and themes in each comic. Clicking on one of the tags will take you to a page showing all comics with that tag. So if Business Frog is your favorite, you can easily find all the comics with him in them, for example.

Unfortunately this means I have to go back through over 250 comics and tag them all. That process might take me a few days, so you won't find tags on the older comics right now. I'll get to them, though, don't worry.

In other news, I also added a dedicated database server to my set of virtual machines hosted by Digital Ocean. This takes a bit of load off the web server and the comics are actually more responsive now. If you're a regular visitor you might notice the speed improvement.

But now the most important thing - pictures of my daughter's stuffed frog from from the trip last week!

In the airport early Monday morning.
Coffee and a cheese danish in Texas.
Just hanging out in the hotel.
Watch out for falling ice!
I had a cold, so Froggy gave me some medicine.
The view at night from our hotel.
Taking a walk in Prairie Creek Park.
A nice pedestrian bridge over Prairie Creek.
Enjoying some pizza the night before we left.
Long layover in Detroit, but they have a cool glowing tunnel.
If you've made it this far, why not click the link below and read today's comic? comic for 14 March 2016

Friday, March 11, 2016

A Rain Effect Using Only CSS

Today's comic contains something that I've never used before in a comic - CSS3 Animation. The rain effect in the first 3 frames is done purely with CSS! Here's how it works.

All the raindrops in each frame are just DIVs with a color gradient background.

<div class="drop></div>

The CSS style for a drop contains a special property, animation. The browser-specific versions, -webkit-animation and -moz-animation, are also specified to catch older browsers.

.drop {
    -webkit-animation: fall .68s linear infinite;
    -moz-animation: fall .68s linear infinite;
    animation: fall .68s linear infinite;

The format of the animation property's value is: <name> <duration> <timing function> <iterations>.

The name of the animation is whatever you want. Mine is called "fall" because that's what raindrops do. I specified a duration of 0.68 seconds, because I played around with it and just liked that duration the best. The timing function here is linear. That means there is no easing of any kind; the drops always move at a constant speed. Finally, I specified "infinite" for the number of iterations. The reasons for these values should make sense shortly...

I gave my animation a name, but I also need to specify what an animation with that name actually does. To do that, you use another CSS directive called @keyframes. (you can also use @-webkey-keyframes and @-moz-keyframes for older browser support)

@-webkit-keyframes fall {
    to {margin-top:700px;}

@-moz-keyframes fall {
    to {margin-top:700px;}

@keyframes fall {
    to {margin-top:700px;}

Here I am specifying that an animation named fall should alter the margin-top property until it reaches a value of 700px. Once that value is reached, one iteration of the animation is considered to be finished. Since I specified infinite iterations above, it means that every time the animation ends it will just reset to the original value and run again. The time it will take to change the margin-top value from whatever it starts at to 700px will be equal to the duration specified earlier.

All I had to do was give the raindrops a random position when I created them, and by animating an increase in their top margin they appear to fall past the bottom of the frame before being reset.

The end result is the beautiful rain effect you can see by clicking the link below and reading today's comic! CSS3 animations can be an easy alternative to JavaScript based animations. They're actually very well-supported by most browsers, even IE 10. comic for 11 March 2016

Wednesday, March 9, 2016

Searching my Comic Archive

I have so many comics now (252) that finding one in the archive is getting hard, even for me. And what if you want to find all the comics that have Science Frog, or Pirate Frog? It's not easy. How many comics have embedded games? Who knows?!?

To correct this problem, I've decided to add tags to all the comics. Whenever I make a new one, I'll tag it with the characters, the theme, a special feature, or whatever else makes sense. You'll be able to see these tags at the bottom of the comic. Like all tags these days, they'll have # in front of the word. Clicking on one of the tags will take you to the archive page, filtered to just show the comics with that tag.

A test page showing my new comic #tags

I'll also be able to put a filter link directly on the Archive page, so you can find comics that contain a search term or have a common tag such as "#game" or something. These are exciting times for frog comic lovers everywhere.

Of course, these new features aren't available yet on the public version of, but they will be soon. I'm still working out some things, but I'm also sitting in a hotel room in Dallas this week. I can never seem to be as productive when I'm away from home. You can still view today's comic without the tags at the bottom; just follow the link below. comic for 9 March 2016

Monday, March 7, 2016

Are There Still Tech Books?

I had to fix a dresser drawer the other day. We're using it in my newest daughter's room, but it's really old. It was actually mine when I was a kid. The track guide broke off the middle drawer because the wood was badly damaged, probably from something I did to it in my youth. Part of the repair required me to use some glue and I asked my 10-year-old daughter to bring me some books off the bookshelf to put weight on it while it dried.

The books she brought me made me wonder about technology books today. I have some old(ish) books on the shelf that I bought who-knows-when, and looking at them piled on top of an old drawer made me think about how I haven't bought a technology book in a long time. And this stack in front of me is worthless now!

ASP In a Nutshell. Almost embarrassed to admit owning this one. Printed in 1999. I had another ASP book that I got rid of a few years ago. I think I kept this one just for the snake on the cover.

The JDK 1.4 Tutorial. Really came in handy back in 2002. The best part is the woman on the front who looks like she's trying to pull off a Medieval Ewok cosplay.

Another classic from 2002, Mastering Enterprise JavaBeans gave me a lot of code that I am still maintaining today. Unfortunately.

The most modern book in this stack, Ajax in Action dates to 2006. I remember excitingly opening this one to learn about the new asynchronous method of bridging the web client and server. I ended up feeling a little bit disappointed as it didn't really tell me anything I hadn't learned on my own in the previous few months.

A quick look around tells me that they still do in fact publish books on software technologies. But I have to wonder who buys them, with so much information available for free on the Internet. But it's harder to glue a drawer with the Internet.

Be sure to read today's comic. Since so many of them have special web-only features, they'll most likely never be published in a book. You can only read them online! comic for 7 March 2016