How to Hack the Developer Console to be Needlessly Interactive

published by Eric Mill on

While I was making isitchristmas.com better for 2013, a stray interaction with Garrett Miller about how he put a colorful easter egg in the developer console for Mapbox sent me on a journey of console self-discovery, which resulted in me putting way too much time into hacking the developer console this year.

This makes use of four tricks: applying basic text styling to console.log output, hacking in sprites through background-images, executing code without using parentheses, and suppressing return values.

CSS in the console

This isn't really a trick, but I don't know that enough people are aware that console.log output can be styled with CSS using a %c flag, in Chrome and in Firebug. It looks like this:

Sadly, Firefox's native developer console doesn't support this (but it will in Firefox 31!), and nor does Internet Explorer. I'm told Safari does. Fortunately, Chrome is by far the most used browser for my site, so I'm fine with this. I have a simple console.log wrapper that "downgrades" the console to plain-text in Internet Explorer.

Creating sprites

While you can't use %c for much past basic text styling, you can apply a background image to spaces, and achieve some crude sprites. You can't set a width — just pick the best number of spaces — but with a background-size of cover, it should roughly do what you want.

I use this on isitchristmas.com to display flags representing where people are from:

And I added full support for emoji in chat messages:

The number of spaces you want will depend on how high your text is. At the default text size, I found 2 spaces to work perfectly for emoji, and 3 spaces to work well enough for flags on isitchristmas (though some wider flags get cut off).

In practice, using inline CSS in JavaScript is annoying. It helps to keep a table of styles and style generators handy, like this:

var styles = { spam: "color: #336699", please: "color: #336699; font-weight: bold", emoji: function(emoji) { return "background-image: url(\"https://isitchristmas.com/emojis/" + emoji + ".png\"); background-size: cover"; } };

Which can be used like this:

console.log( "%cPlease%c don't spam and ruin the chat! %c %c %c %c %c ", styles.please, styles.spam, styles.emoji("smiley"), styles.spam, styles.emoji("heart"), styles.spam, styles.emoji("christmas_tree") );

Which produces:

Running code without parentheses

If you define a function and run it without its parentheses, it will just print the function out:

And of course, if you execute a bare word that's not been defined, the console will throw a ReferenceError. (Sadly, you can't catch and repurpose errors thrown in the console itself with window.onerror.)

However, the console always tries to print out the return value of the line you execute. You can take advantage of this by making your variable a no-op function, and then overriding that no-op's toString with a function that does whatever you want.

I use a function as the base to call toString on, because as it turns out, the console will ignore attempts to override toString on objects and strings. And of course, this isn't documented behavior at all — it could change any time!

Suppressing return values

In the example above, the console spat out "undefined" after running help. That's because console.log returns undefined, and the console always tries to print the return value.

You can cause the console to print blankness instead, by returning a single space at the end of your toString override.

Note that this tasteful blankness only works inside of a toString override! At the end of a normal function, with parentheses, returning " " will just print out an obvious " ". So, if you're running a normal function, and you want to suppress output, you need to do something like this:

More useful is putting these tricks together. Since a chat room means writing a message and passing it as an argument, I've been asking users to use proper functions — e.g. say("Hi!") — but that means people have to type parentheses and quotes all the time, which can be annoying and confusing.

I can use the toString trick to provide two methods of chatting - one with parentheses, and one without. If you use say without parentheses, it will pop up an ugly (but functional!) traditional JavaScript prompt window for input, and it'll then feed that input into the real say() function.

This is what the code looks like:

var say = function(message) { // ... code to send message ... return blank(); }; say.toString = function() { say(prompt("What do you want to say?")); return " "; };

And I document it like this:

There is no good reason to do this

The developer console is absolutely not meant for this stuff, and it is all a completely silly endeavor. But it was fun to figure out how to do, and I think the Internet is better with more easter eggs in it (and the folks at Console Message obviously agree).

Maybe there's a useful library to extract from all this, maybe not. Regardless, I hope this crucial and painstakingly crafted engineering advice is helpful in your professional endeavors!

Update: I came across console.image, a fun little library for console images that takes the hacks further, into even more grotesque and wonderful territory. Maybe more usefully, the same author also created console.snapshot, a very impressive way to snapshot both data and images from the DOM, into the console.