Friday, October 30, 2015

Starting a New Game

I've been kicking around the idea for a while now, but tonight I finally started a new game for Amphibian.com. Actually starting a new project is almost as difficult as finishing one, but if I never start there's no chance of ever finishing.

I don't have any code or pictures to share yet, but this one is going to star Science Frog. I'm trying to make it in the style of a NES Megaman game, and I happened to find a great resource for information about old video game physics.

TASVideos is a site which, among other things, catalogs lots of data about how the physics worked in games to assist speedrunners. This page on the NES Rockman game (also known as Megaman) has a great breakdown of the horizontal and vertical speed of Megaman as well as the gravity constants and lots of other good information. With this, I should be able to make Science Frog's jumps mimic Megaman's.

So far though, these numbers have not resulted in any great achievements. The jumps are still all out-of-whack probably due to my inability to get the code right using Phaser. I'll keep trying and hopefully by Monday I'll have something to show (along with some pictures of my kids in their Halloween costumes).

And speaking of Halloween costumes, since tomorrow is Halloween I have a Jack-o-lantern theme today in the comic (and the color-change animation effect I talked about on Monday!)

Amphibian.com comic for 30 October 2015

Wednesday, October 28, 2015

Bit Rot. Is it real?

Have you heard of bit rot before? It's the perception that software (the bits) will degrade (or rot) after sitting around for a long time. It will begin to malfunction despite no apparent changes in the environment. I've been writing software long enough to know that it doesn't change on its own, but I've experienced bit rot before. It sure seems real, despite the logical portion of my brain telling me that it can't be.


It sounds crazy, but a system that hasn't been used in a long time just stops working. We have a system at work which we use every few months for demonstrations or training and in between those times it sits idle. Every time we go to use the thing, we find it to be completely crashed in some weird way. Did it get lonely?

And what about the speed of a computer? Have you noticed that they just seem to slow down over time, even if nothing else changes? I had a computer that was new in 2008 and had no real upgrades performed on it for about 5 years. No new software, no upgrades to the operating system. At the end, it was so slow as to be practically unusable. Was it always that slow and I just didn't notice because every other computer I used was similar in speed? Or did it actually slow down over time? I'm fairly certain my cell phone is slowing down as well. Does that count as bit rot too? Or is that something else?

I have no explanations, but this phenomenon can't be ignored. It can be talked about in frog comics though.

Amphibian.com comic for 28 October 2015

Monday, October 26, 2015

Animate Colors with jQuery

While working on an upcoming comic, I wanted to add a color-change effect to an element. I soon learned that jQuery's animate() function doesn't support animation of non-numeric CSS properties. So while I can animate a width between 100px and 200px, for example, I can't animate a color between #000000 and #FF00FF.

But all is not lost! There is a plugin, appropriately named jQuery Color, which adds the color animation feature. It is trivial to use and extremely small to download.

To start using it, just download the .js file from their GitHub page. I am using the minified version. Include it in your page after jQuery. Then, just animate colors the same way you would animate other CSS properties and it will magically work!

In the following example, the page starts out containing a DIV with a black background. As soon as jQuery is ready, an animation changes the background color to purple over a span of 10 seconds.

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Color Animation</title>
</head>

<body>

  <div id="test-div" style="width: 500px; height: 300px; background-color: #000000;"></div>

</body>

<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://code.jquery.com/color/jquery.color-2.1.2.min.js"></script>
<script>

$(function() {

    $('#test-div').animate({backgroundColor: '#FF00FF'}, 10000);

});

</script>

</html>

It doesn't get much easier than that! But what comic prompted me to use this? It's not for today's. You'll just have to wait to find out (or look in the repository on GitHub).

Amphibian.com comic for 26 October 2015

Friday, October 23, 2015

Froggy Pictures from Texas

You may not know this, but whenever I travel away from home I always have a plush frog with me. This started way back, probably 8 years ago, when my daughter Alex told me to take her Froggy with me on a business trip. I took pictures of him doing various things with me, and continued the tradition with every subsequent trip. When our second daughter was born, Alex gave Froggy to her but he still comes with me on trips.

Since I've been in the Dallas, Texas area this week, I thought I might as well share some of the Froggy pictures. This frog has been many places. This is his third time in Texas.

Plush frogs like to take selfies, just like people.

He likes to eat green foods. We eat dinner together every night.

He helps me write my blog. His typing is poor.

I often find him sitting by the pool.

We sometimes share a pizza. This one was pretty good.

Big storm in the Dallas area tonight. Froggy watches the rain.
Froggy was not the inspiration for Amphibian.com. I was drawing frogs and putting them on websites years before Alex was born. In fact, I started drawing frogs when I was 11 years old, just a year older than Alex is now. Which is probably why someone thought a plush frog would make a nice gift for my newborn daughter (Froggy is technically a Mary Meyer Sweet Rascals Freda Frog if you want one of your own. They're getting hard to find nowadays).

Amphibian.com comic for 23 October 2015

Wednesday, October 21, 2015

Bleeding Edge JavaScript Math

In today's comic, Science Frog finds himself on the bleeding edge by accident - literally. Just so I have a nice tie-in with that, I'm going to share some bleeding edge JavaScript math functions. Not literally. You won't actually bleed. Unless you cut yourself on your keyboard, in which case you really should get a better keyboard.

Why do I say this is bleeding edge? I guess I could say cutting edge or leading edge. I could also just drop the idioms and say that these are new language features. The first is only available in the Firefox nightly builds at the moment. How's that for the edge of some kind?!

What is that new feature? An exponentiation operator! That's right, no more need for Math.pow()! Just use double asterisks. This is a feature currently planned for ECMAScript 7 next year, but you can try it in a Firefox nightly build today (it may be included in next month's standard release):

var e = 2 ** 8;
var p = Math.pow(2, 8);

console.log(e === p);  // prints true

Not exciting enough for you? Come on, Java doesn't even have an exponentiation operator and it's been a legitimate language for at least 10 years! I guess Python does, but now all you Python devs don't have an excuse for not switching to JavaScript.

Now for something slightly less edgy. Since Math.pow() will become obsolete, I guess it's only fair that it get a new function. Added as part of ECMAScript 6 this year, Math.fround() is that function. Given a number, it returns the nearest single-precision floating point number. What? Try this (it works in standard Firefox releases since 26 and Chrome since 38):

var x = Math.fround(2.567);
console.log(x); // prints 2.566999912261963

var y = Math.fround(3);
console.log(y); // prints 3

var z = Math.fround(3.3);
console.log(z); // prints 3.299999952316284

I'm sure it makes sense now - Math.fround() gives you the most precise floating point equivalent of a number.

That's all I have to share today. I'm actually away from home this week - in the Dallas area for some software architecture training. But I did all the comics for this week before I left, so no problems there! Missing the comics is fround upon. Sorry, couldn't resist that one.

Amphibian.com comic for 21 October 2015

Monday, October 19, 2015

JavaScript Collections - Set

In addition to Map, JavaScript also got another popular collection type this year - the Set. As part of ECMAScript 2015, which is now mostly supported by Node.js, this new build-in object type was added to the language. It's also available on the client-side in Firefox, but it's probably best to keep it on the server side for right now.

So how does it work? Pretty much like you'd expect a Set to work. You can add values to it, but it can only hold unique values (contrast this with Array, which can hold multiple of the same object).

var s = new Set();

s.add("string value");
s.add(4);
s.add(false);

console.log(s.size); // prints 3

console.log(s.has(4));               // prints true
console.log(s.has("not in there"));  // prints false
console.log(s.has(3+1));             // prints true
console.log(s.has(false));           // prints true

s.add(4);            // no effect
console.log(s.size); // still prints 3

s.delete(4);
console.log(s.size); // prints 2

As you can see from the example, it supports the standard functions for adding, removing, and checking. Note that, just like Map, the type of objects that can be put in a Set are wide open. You can keep Strings, Numbers, Booleans, and even Functions in the same set. The Booleans are interesting - look at line 5 above. You can add false to the Set. Then s.has(false) returns true. But the fun doesn't end there. You can even put Infinity (NaN) in the Set if you want. Why would you want to? I have no idea, but it works.

var b = new Set();

b.add(4/0);
b.add(3/0);

console.log(b.size); // 1, Infinity added twice

console.log(b.has(1/0)); // prints true

Okay, enough of that. What else should you know about Sets? You can iterate over them just like Arrays.

var c = new Set();

c.add("one");
c.add("too");
c.add("tree");

c.delete("too");
c.add("two");

for (i of c) {
    console.log(i); // one, tree, two
}

The iteration will produce the contents in insertion order, which is why the above example prints one, tree, and then two. There's not really a way to re-order the Set.

Just like I said about Maps, I haven't really been missing them from JavaScript. There's plenty of ways to get around their absence. But now that they're part of the language, I'm sure I'll end up using one somewhere. Personally, I think Sets add more to JavaScript than Maps do simply because of the unique item enforcement.

Don't miss today's Amphibian.com comic. Laughter keeps you healthy.

Amphibian.com comic for 19 October 2015

Friday, October 16, 2015

JavaScript Collections - Map

I still can't fold them back up!
Ready to be confused? Okay, so you know that JavaScript has a map function on Array objects. It creates a new Array by applying a given function to each element of the Array. But now it also has a Map collection object. Now when you talk about a Map in JavaScript, are you talking about a function on an Array or a key/value pair data structure?

Those of us who did a lot of programming in other languages before JavaScript are probably familiar with classic data structures like the Map. A Map is a (typically) indexed collection of keys and their associated values. If you know the key, you can grab the value. C++ has collections objects, as does Java. JavaScript has not had a native Map until recently, but I've never really missed it. Why? Keep reading.

In many of the newest browsers and in Node 4, we can now use Maps. Their functionality should come as no surprise to anyone familiar with basic data structures found in other languages.

var m = new Map();

m.set("key1", "value1");
m.set("key2", "value2");

console.log(m.size); // 2

console.log(m.get("key1")); // "value1"
console.log(m.get("key3")); // undefined
console.log(m.has("key2")); // true
console.log(m.has("key4")); // false

But this is JavaScript! We needn't limit ourselves to just one type of key or value! Check this out:

var p = new Map();

// function as a value...not too strange:

p.set("f1", function(x) { return x*42; });

console.log(p.get("f1")(2)); // 84

// function as a key...a bit odd:

var w = function(z) {
    return z % 3;
};

p.set(w, "weird");

console.log(p.has(w)); // true
console.log(p.get(w)); // weird

Sure, it's perfectly legal code...but why? I know I'm not the world's smartest software engineer, but I cannot think of an example where putting a function as the key in a Map would make any sense. If you can think of one, please comment below.

Now we're having fun with Maps and we're all excited about JavaScript getting this great new data structure. But this is not a whole lot different from what we could always do in JavaScript with objects, since objects behave pretty much like Maps.

var h = {};

h["key1"] = "value1";
h["key2"] = "value2";
h["func1"] = function(t) { return t / 8; };

console.log(h["key1"]);      // value1
console.log(h["key3"]);      // undefined
console.log(h["func1"](16)); // 2
console.log(h.func1(32));    // 4

See? Pretty much the same as my first Map example up at the top. Fine, plain Objects have no size field...so I suppose that's one thing that Maps offer. There are also some differences is how iteration is handled. Consider the following example:

var myMap = new Map();
myMap.set("k1", 33);
myMap.set("k3", 55);

var myObj = {};
myObj["k1"] = 33;
myObj["k3"] = 55;

for (e of myMap) {
    console.log(e);    // the key and value as an array
    console.log(e[0]); // just the key
    console.log(e[1]); // just the value
}

for (e in myObj) {
    console.log(e);        // just the key
    console.log(myObj[e]); // the value
}

Iterating over the Map with for (e of myMap) gives you values for e that are 2-element arrays containing both the key and the value for each Map entry. Iterating over the Object with for (e in myObj) gives you values of e that are just the key.

I'm not going to tell you that JavaScript Maps are the greatest thing since sliced bread, but maybe sliced bread was never that big of a deal anyway. It's not like people didn't have knives. And speaking of bread...

Amphibian.com comic for 16 October 2015

Wednesday, October 14, 2015

Technical Debt

I've seen the term "technical debt" come up at least a dozen times in the past week, so I had to write a comic about it. I first heard this concept described about 9 years ago. We were preparing a demonstration of a system at work, and one of the components was not ready. To make the system function end-to-end for the demo, one of the members of the team created a mocked-up version of it that consumed inputs and produced outputs well enough to fool the rest of the components into functioning. The problem was that to the viewers of the demonstration it looked like we had already solved all the hard problems that should have been contained inside that component.

We made it clear what the actual state of the system was during the demo (and like most demos, I think it crashed while being presented). If we hadn't explicitly explained the situation to the customers, they may have concluded that the system was much more mature than it was in actuality. In such a scenario we could have incurred a great amount of technical debt, as there would have been pressure to ship the product before it was complete. In this example, the potential technical debt seemed like just plain old deception to me. But analogous to monetary debt, it could also be thought of as just taking out a loan. A loan of confidence in the development team. If we couldn't pay it back, we'd all end up in Technical Debtors Prison.

I know now that technical debt has many more flavors to it, and it's not always a bad thing. At the beginning of any project, the technical debt is the greatest. You have nothing but a promise to deliver a working system. As time goes on, hard work and smart decisions should pay back the debt. It might ship before the debt is completely paid off, but the next release can pay off a little bit more.

Similarly, I owe this blog some code. I haven't done much development outside of work in the past week, but hopefully that changes soon. In the mean time, continue to enjoy my comics and accept my I.O.U. for more software development examples and instruction.

Amphibian.com comic for 14 October 2015

Monday, October 12, 2015

My Online Degree

Much like the frogs in today's comic, I got a Master's degree online. Unlike the frogs in today's comic, I was using the Internet to do coursework.

I received my Master's Degree in Software Engineering from Penn State University in 2012 after completing two years of online instruction. Penn State calls these types of programs their "World Campus" because the students can literally be located anywhere in the world. That part alone creates some unique challenges, but more on that in a minute.

Overall, I would say that it was a good program. I feel like I learned a lot of extremely practical aspects of the software engineering discipline. This particular program has a heavy focus on real-world applications for professionals, and less on research and writing papers. This suited me. I feel like talk is cheap and I much prefer building stuff. Even though I write on here three times per week...

Even though all my classes were online,
I did go to a real-life graduation.
I would not say that the program was perfect. It was difficult, especially for me. I know it was probably stupid of me, but I waited 10 years after finishing my Bachelor's Degree to start my Master's. When I started in 2011, I had two children. One was only two years old. When I finished in 2012, I had four children (keep in mind, we're foster parents). I had a very demanding job that required sometimes long hours as well as international travel. The whole "world campus" thing worked well for me when I was in the United Kingdom and could do my coursework just as if I was at home, but was really inconvenient when every member of a 4-person project group was located in a different time zone. Scheduling group meetings was a challenge.

And let me talk about group meetings for a minute. Now, if any of my former classmates happen to read this, I want to be clear that I didn't dislike working with any of them specifically. But in general, I hated the group work. Maybe I just hate working in groups. I think I chose a career in software engineering under the assumption that I would work with computers all the time. As it turns out, I work with people all the time and I wonder if I chose the right path in life. But when I'm not at work, I like to not be in groups. At least virtually, I was robbed of that for two years by this degree.

The pace of the program was relentless. To get 36 credits completed in two years, I had to take two 3-credit courses in the Spring, Summer, and Fall semesters. Why is there no Winter semester? The Spring semester starts in January when it is clearly Winter and only ends in the Spring. The Fall semester actually takes place almost entirely in the Fall. Same with Summer. Spring semester is completely misnamed. What's my point? I never got break from classes. They just kept coming and coming and coming. I guess I have another issue, related to this. Because it took all of my free time for years, I feel as though I missed out on a lot of "real" learning. As in, the opposite of "book" learning. For example, right before I started grad school I was doing some work with Node.js, which was still in its infancy. I had to totally abandon all of my personal learning endeavors while I was working on my degree. When I came back to Node, it had come quite a long way. Who knows, maybe if I hadn't gotten a Master's Degree I could have instead become a Node expert. Or I could have started a web comic years earlier. Something.

What's the bottom line? Am I glad I did it? I guess so. Am I glad that it's over? Absolutely!

Are you glad there's a comic for today?

Amphibian.com comic for 12 October 2015

Friday, October 9, 2015

First Impressions from my TopWebcomics.com Ad

Here's my ad, but you don't have to click on it.
As I mentioned last week, I am running a month-long ad banner on TopWebcomics.com. It just started appearing on Wednesday. In the past, I tried promoting Amphibian.com on Facebook but did not get really great results. My hope is that advertising on a site devoted to web comics will have provide better returns.

Okay, first of all let's address the irony of running ads to promote a comic that I refuse to monetize with ads. I don't think I'm too much of a hypocrite. Right now, I don't need ad revenue to fund the server that runs Amphibian.com so I am free to try other methods of earning money from the comics. And I don't hate all ads on the Internet, I just feel like in many cases they've become so horrible that they actually detract from the content on many web pages. Ad blockers have only gotten more popular because ads have gotten more horrible. My decision to pay for ad promotion on TopWebcomics helps their site, which has already been driving a fair amount of traffic to my comics (as long as I stay near the top of the 2nd page). Even if no one sees my ad because they're all running ad blockers, I feel like I'm giving back to the site.

So how many people have seen the ad?

Well, I don't know. Unlike Facebook advertising, I don't know how many people have seen it. But I can tell how many comics have been viewed because of it. I am using a referral code in the banner's URL and a cookie to track how many times a person who came to the site by clicking on the ad views a comic for the next month.

So far, over 800 comics have been viewed as a direct result of the ad. That's approximately 29% of the comics viewed since the ad started running just 48 hours ago. Not a bad start.

Other interesting things to note
  • A majority of the people coming from the ad are jumping back to the first comic and reading the early ones as well as the current ones.
  • There has been a significant increase in the percentage of visitors to the site who are using iOS devices. Maybe my mobile-first approach to webcomics will finally pay off?
  • Spiders are not a problem. Is it because they don't do cookies, or follow ad banner links, or include query strings on links they follow? I'm not super-knowledgeable in this field of Internetting, so I don't know. I'm only reporting what I see.

That's all for today. I hope you like today's comic, which pokes fun at ageism in the software industry (at 36, I'm starting to feel it) by making clearly absurd claims as to the origin of new programming languages. Or maybe I'm on to something...

Amphibian.com comic for 9 October 2015

Wednesday, October 7, 2015

Future JavaScript - Generator Comprehensions

A comprehension generator?
If you enjoyed my look ahead at a JavaScript feature of the future on Monday, then you're in luck. If not, well, please read the rest of this post anyway.

That's right, I'm going to talk about one more upcoming language feature likely to be part of the ECMAScript 7 specification. If you like generator functions and you like array comprehensions, you're going to love Generator Comprehensions!

Maybe.

Not just another weird mashup, Generator Comprehensions have a syntax almost identical to Array Comprehensions, but create simple generator functions based on iterable objects instead of new arrays. Generator functions, if you recall, are a new type of JavaScript function which exit and re-enter while maintaining their state. If your generator function needs are very simple, creating one with a comprehension may be desirable.

Take a look at the simplest of examples to understand how they work. Important note! Just like with Monday's Array Comprehension examples, these only work on Firefox 30+. Sorry Chrome/Node fans.

var data = [ 1, 2, 3, 4, 5 ];

var gen = ( for (n of data) n * 2 );

console.log(gen.next().value); // prints 2
console.log(gen.next().value); // prints 4
console.log(gen.next().value); // prints 6

The Generator Comprehension appears on line 3. It looks almost exactly like an array comprehension, but uses parentheses ( ) instead of square braces [ ] to delineate it. Inside the parentheses, it takes the form for (n of iterable) statement, where the iterable in this case is an array and the statement is multiplying n by 2. The generator created will yield the result of the statement with the next value of n each time it is invoked. It is equivalent to this code:

var data = [ 1, 2, 3, 4, 5 ];

function* doubler(a) {
    for (var i = 0; i < a.length; i++) {
        yield a[i] * 2;
    }
}

var gen = doubler(data);

console.log(gen.next().value); // prints 2
console.log(gen.next().value); // prints 4
console.log(gen.next().value); // prints 6

I suppose you'd have to agree that the comprehension version saves a few lines of code. And fewer lines means fewer chances to make a mistake, right? Maybe. But remember, if you overuse all this slick, super-short syntax, your code becomes less readable and therefore less maintainable. Trade-offs.

I should also mention that just like Array Comprehensions, the statement part of the expression can include an if. So if you wanted a generator that produced just the leap years from a given array of years, it might look like this:

var years = [ 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 ];

var leapYears = ( for (y of years) if ( y % 4 == 0 && (y % 100 != 0 || (y % 100 == 0 && y % 400 == 0)) ) y );

console.log(leapYears.next().value); // prints 1996
console.log(leapYears.next().value); // prints 2000
console.log(leapYears.next().value); // prints 2004

Of course, cramming the leap year logic (divisible by 4 but not divisible by 100 unless also divisible by 400) into a single if statement just trades vertical code for horizontal code - and horizontal code really bothers me. Call me old-fashioned, but I like it when the lines are no more than 80 characters wide. But everyone is entitled to their own code style - unless you have to work on a team! Then you better do what's agreed upon by the group. Just sayin'.

Now for some commentary. While Generator Comprehensions are a fine way to create generators in a single line of code, I feel like the real power of generator functions is in having them encapsulate logic that can not (or should not) be expressed as a single line. If I just want to iterate over an array and even perform some basic translation of its values, I'm not sure that a Generator Comprehension would be my first choice. At the risk of sounding like a software dinosaur, there's nothing wrong with for loops when done correctly. Their intent is more clear, which makes maintenance less costly. I worry about those kinds of things because I'm a software engineer, not a hacker.

I'm also an author of frog comics, which means I worry about you clicking on the link below.

Amphibian.com comic for 7 October 2015

Monday, October 5, 2015

Future JavaScript - Array Comprehensions

I can't wait any longer!
I've spent a bit of time lately writing about new ES6 language features now available in Node 4, but JavaScript doesn't end there. The ECMAScript 7 specification will probably be out next year and should include even more wonderful things. I thought I'd take a look ahead today at the one I'm most excited about, Array Comprehensions.

An Array Comprehension, a feature already available in other languages, is just an easy way to create a new array from an existing one. JavaScript gives us other ways of doing that, such as map, but comprehensions provide a very tight and sensible syntax for a specific job.

Consider the following example. If you want to run this yourself, you'll need Firefox 30+. It is supported neither by other browsers nor Node at this time.

var a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

var b = [ for (n of a) if (n % 2 !== 0) n ];

console.log(b); // prints [ 1, 3, 5, 7, 9 ]

The array comprehension syntax can be see on line 3, creating a new array b containing just the odd numbers in array a. I like the syntax because the end caps [ ] are square braces, just like an array. Since a new array is the result of the expression, it makes sense. Inside the braces, it is very simple. You have for (n of a), which is the standard iteration syntax. The next part of the expression will be evaluated for each member of a, and for each evaluation n will be the current member. Whatever this expression is will be the value added to the created array. In this example, it will be only values of n which are odd. But consider this example.

var c = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

var d = [ for (n of c) n * n ];

console.log(d); // prints [ 1, 4, 9, 16, 25, 36, 49, 64, 81, 100 ]

No ifs here, the new array will just contain the square of each number in the original array. But you don't even need to care about what's in the original array, technically speaking. Look at this.

var e = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

var f = [ for (n of e) "nothing" ];

console.log(f); // prints a lot of "nothing"

Rather useless, that one. But you could call a function instead...

function doSomething(p) {
    if (p % 2 === 0) {
        return p * p;
    } else {
        return p * 2;
    }
}

var x = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

var y = [ for (n of x) doSomething(n) ];

console.log(y); // prints [ 2, 4, 6, 16, 10, 36, 14, 64, 18, 100 ]

Here's a catch, though. What if you want to zero-out the even numbers from the first array? So instead of returning a new array with just the odds, returning a new array with odds and zeros in place of the evens. You can't use the if...else syntax inside the braces.

var g = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

// this form does not work (invalid syntax):
var h = [ for (n of g) if (n % 2 !== 0) { n } else { 0 } ];

Instead, you have to either call a function or make it a single statement using the shorthand syntax.

var g = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

// this form works:
var h = [ for (n of g) (n % 2 !== 0 ? n : 0) ];
console.log(h); // prints [ 1, 0, 3, 0, 5, 0, 7, 0, 9, 0 ]

// so does this:
function zeroEvens(n) {
    if (n % 2 === 0) {
        return 0;
    } else {
        return n;
    }
}
var j = [ for (n of g) zeroEvens(n) ];
console.log(j); // prints [ 1, 0, 3, 0, 5, 0, 7, 0, 9, 0 ]

Lots of fun, am I right? Well, it's too bad we'll have to wait quite some time to have this much fun in production. Unless you are writing JavaScript for a 100% Firefox environment, you'll have to be patient. It's unclear when ES7 will be official and be supported by other browsers and some future version of Node. Maybe next year sometime? Anyway, this gives you something to look forward to. Like how you look forward to new Amphibian.com comics.

Amphibian.com comic for 5 October 2015

Friday, October 2, 2015

Tracking Visitors from Ads with Express Middleware

I'm getting ready to run another month of promotion for Amphibian.com. This time I'll be running an ad on TopWebcomics.com, a webcomics list and voting site.

The best list of active web comics, including mine!
Unlike before, when I ran ads on Facebook, I'd like to get a better idea about how people interact with the site after coming from an ad banner. This is sometimes called conversion tracking, but since I'm not really trying to convert any clicks to purchases I'm not sure the term applies. I am trying to convert people to regular readers of the comic, so maybe it does. In any case, I added a piece of middleware to my Express web app in order to track visitors from ads.

Amphibian.com is built using Node with the Express framework. I use various Express middlewares to add features to the site, such as access control, logging, multipart uploads, etc. I've written quite a bit about these in the past (here, here, and here, for example). I wrote a custom middleware and modified my logging middleware in order to create this ad tracking feature.

Here's how it should work... I create my ad banner and give it to a site to display, along with a URL. Instead of just plain http://amphibian.com, I assign each ad its own unique code and append that to the URL as part of the query string. For example, I tell TopWebcomics that I want my ad banner to link to http://amphibian.com/?s=abcdefg. When I see a value for s (I use "s" for "source") on the query string of any comic access, I add the value (abcdefg in this example) to the request object in a special field. Then in my logger, I look for a value in that field and write it to the database along with the rest of the request data. Now I'll be able to query my database later and determine which accesses originated with a user clicking on the banner. If I ran more than one banner at a time, I could just give them each a different code and compare their performances.

My tracker middleware looks like this:

module.exports = function() {

    var tracker = function(req, res, next) {

        var src = null;

        if (req.query.s) {

            src = req.query.s;
            res.cookie("s", src, { maxAge: 2629746000 });

        } else if (req.cookies.s) {

            src = req.cookies.s;

        }

        if (src !== null) {
            req["click-source"] = src;
        }

        next();

    };

    return function(req, res, next) {
        tracker(req, res, next);
    };

};

The code is fairly simple. First I check the query string for an s field (line 7). If present, I set the source code variable to its value and set a cookie with the same value. The cookie has an expiry time one month in the future. If the request does not have a s value in the query string but does have the cookie value, I set the source code variable from the cookie. This is how I know if you come back to the site later without clicking on the ad - you'll have the cookie. As long as you clicked on the ad once, I count the ad as being the source of all your visits for the next month.

The source code gets put on the request object in a field called "click-source" there on line 19. This is the field that I check in the logger middleware and, if found, write to the database.

One final note about using this middleware. Since it relies on the cookie-parser middleware to populate the request's cookies field, I have to make sure that I set my Express app to use the cookie-parser first. Express chains the middlewares together in the order they are declared.

var cookieParser = require('cookie-parser');
var adTrack = require('./sourceTracker');
var logging = require('./logger');

// ... other stuff ...

app.use(cookieParser()); // required before source tracker
app.use(adTrack());
app.use(logging());

// ... other stuff ...

So my plan for tracking conversions is simple, and only took a few minutes to implement. After I run this ad for a month I'll be sure to write up my results and what, if any, improvements I want to make for the next time I try a promotion. Until then, keep reading the comics by clicking on the link below.

Amphibian.com comic for 2 October 2015