Tag: GIF

Party Gopher!

June 13, 2018 » Geek

The Go slack has a cute little dancing Gopher that appears to have come from Egon Elbre. I love it!

Dancing Gopher

This little dancing Gopher made me think of Party Parrot, so I wanted to parrot-ize him. Normally I might just open up Gimp and start editing, but this is the Go Gopher, we can do better than that!

My plan was to use Go’s image packages to edit each frame and replace the blue with the correct parrot color for that frame by walking over the pixels in each frame.

Once I got into the package docs however, I realized that since gif’s are paletted, I can just tweak the palette on each frame and be done. Much simpler. Let’s get into then, shall we?


First things first, I needed to declare the party parrot frame colors, and the light and dark blue that the dancing gopher uses. I grabbed the blues with Sip and I already had the parrot colors on hand. Sure, I could precompute these and declare, but let’s keep it interesting.

Note that I have a DarkParrotColors slice as well, this is for the corresponding dark blue replacements. I generate these with darken which I’ll show in a moment.

var (
	ParrotColors     []color.Color
	DarkParrotColors []color.Color
	LightGopherBlue  color.Color
	DarkGopherBlue   color.Color

func init() {
	var err error

	for _, s := range []string{
	} {
		c, err := hexToColor(s)
		if err != nil {
		ParrotColors = append(ParrotColors, c)
		DarkParrotColors = append(DarkParrotColors, darken(c))

	LightGopherBlue, err = hexToColor("8BD0FF")
	if err != nil {
	DarkGopherBlue, err = hexToColor("82C2EE")
	if err != nil {

Also notable is the hexToColor which just unpacks an HTML hex RGB representation into a color.Color.

func hexToColor(hex string) (color.Color, error) {
	c := color.RGBA{0, 0, 0, 255}

	r, err := strconv.ParseInt(hex[0:2], 16, 16)
	if err != nil {
		return c, err

	g, err := strconv.ParseInt(hex[2:4], 16, 16)
	if err != nil {
		return c, err

	b, err := strconv.ParseInt(hex[4:6], 16, 16)
	if err != nil {
		return c, err

	c.R = uint8(r)
	c.G = uint8(g)
	c.B = uint8(b)

	return c, nil

Here is the darken function, pretty simple.

func darken(c color.Color) color.Color {
	r, g, b, a := c.RGBA()
	r = r - 15
	g = g - 15
	b = b - 15
	return color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}

Now I need to pull in the gif and decode it, all very boilerplate.

	// Open the dancing gopher gif
	f, err := os.Open("dancing-gopher.gif")
	if err != nil {
	defer f.Close()

	// Decode the gif so we can edit it
	gopher, err := gif.DecodeAll(f)
	if err != nil {

After that, I iterate over the frames and edit the palettes.

	for i, frame := range gopher.Image {
		lbi = frame.Palette.Index(LightGopherBlue)
		dbi = frame.Palette.Index(DarkGopherBlue)

		frame.Palette[lbi] = ParrotColors[i%len(ParrotColors)]
		frame.Palette[dbi] = DarkParrotColors[i%len(DarkParrotColors)]

Lastly, more boilerplate to write it out to disk.

	o, _ := os.OpenFile("party-gopher.gif", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	defer o.Close()
	gif.EncodeAll(o, gopher)

Party Gopher

You can grab the code on Github, and thanks again to Egon Elbre for the excellent original gif!

Custom Mailbox Betacoins

August 20, 2014 » Geek

Yesterday, Mailbox released their beta Mac app. One cute thing they did, was that instead of a beta link or code, they distributed cute little animated gif coins which you could then drop into a “tin can” in the app to gain access.

A betacoin

I was intrigued by the concept, so I got some used betacoins from my friends and did a little digging to figure out how they were doing it.

My plan was to diff the coins and see what was changed from coin to coin, but I didn’t even need to do that. A quick inspect with gifsicle revealed an obvious token in the gif comments extension block.

[email protected]:~/Desktop/betacoins ✪ gifsicle -I coin113121.gif '#0'
* coin113121.gif 122 images
  logical screen 173x130
  global color table [64]
  background 44
  loop forever
  + image #0 173x130 transparent 45
    comment "F1699622-5500-4F31-B643-798427D0DBFA"
    disposal asis delay 0.03s
[email protected]:~/Desktop/betacoins ✪ 

From there I checked a couple other coins to see if they had differing comments, and sure enough they did.

So now the question became, could I add the comment from a valid betacoin to another gif and have it still work?

I grabbed a lovely gif of a barfing unicorn off the web, and set to work.

[email protected]:~/Desktop/betacoins ✪ gifsicle unicorns_puke_rainbows_by_chronicle_vindictive-d56nvl0.gif  --no-comments -c '"F1699622-5500-4F31-B643-798427D0DBFA"' '#0' '#1-' > unicoin.gif
[email protected]:~/Desktop/betacoins ✪ gifsicle -I unicoin.gif '#0'
* unicoin-a.gif 9 images
  logical screen 660x850
  global color table [256]
  background 0
  loop forever
  + image #0 660x850
    comment "F1699622-5500-4F31-B643-798427D0DBFA"
    delay 0.92s
[email protected]:~/Desktop/betacoins ✪ 

I then downloaded the beta, crossed my fingers, and dragged the unicoin into the tin can. I was rewarded with a tinkle of a coin dropping in, and access to the beta.

This is a valid betacoin.
This is a valid betacoin.

Turns out, Mailbox could care less what else is in your gif. Just so long as you have a comment with a valid token, it’ll use that gif and animate it prettily.

As an aside, the coin gif has a staggering 122 frames. 122. Sparkles are expensive, yo.

Edit (2014-08-20)

I created a service for changing up your Mailbox betacoins, called Unicoin. You’re welcome.

Second Edit (2014-08-20)


Tags: ,