Pixie App Widget Test

I’m testing out embedding a Pixie app widget in my blog. If it doesn’t show up in your RSS feed try it on the main site.

Here’s a little platformer app that I’ve been making in Pixie. I started it a week ago and polished it up during TIGJam.

This widget embedding is a great way to share games created in Pixie with the world. Additionally, because it is an embedded widget, it receives updates automatically as you update your game!

Quest for Meaning a game made in two days for Mini-LD #21

Here it is, my entry for the mini-Ludum Dare competition. The competition theme is “biggest fear”, and one of my biggest fears is a meaningless life. Not only that, but a meaningless eternity. Pictures for Sad Children has a very similar theme at times and it helped inspire parts of this game (though I couldn’t find a good way to work in “monster most vulnerable when heaving with sobs”).

This was my first 2 day competition and I’ve learned some things. First, two days is a long time. Second, having real tools would make me very, very happy. Third, I thought that doing all the art and all the programming for a game would be hard, but it seems to use different parts of the brain, so when working on art the programming part of my brain is relaxing and vice versa.

This was the first moderately legit game that I’ve done all my own art on (title screen and chest graphics contributed by Lana). Also, my first game with a 4 color grayscale pallet. And additionally, my first major undertaking on the Pixie platform.

Working with the Pixie platform had some serious trade-offs. JavaScript is a surprisingly productive language with it’s functional and dynamic nature, but it has a harsh and brutal syntax. The platform libraries helped a lot to smooth some things out, and as they become more complete it will get better and better. I have high hopes for CoffeeScript, now that it is getting close to 1.0 I’m going to try using it on all my new projects and hopefully never go back. Another advantage was the tight art and code integration. It was trivial to create an image and have it appear in the game seconds later. The biggest drawback of Pixie right now is that the code “editor” is pretty much just a text area. There are no tabs, no integrated source navigation, no auto-save, no version control, and all kinds of other terrible issues. Also, there is no real tile editor, though Noel Berry pioneered the way by using the pixel editor as a tile editor before, and the surprising thing is that it’s actually not too bad.

Using Pixie to make art is awesome, but the game “platform” is not fleshed out enough for me to recommend making an entire game in it to everyone yet.

A special thanks to everyone who helped playtest and discuss various elements of the game throughout it’s stages: Boltz, McGrue, DavMo, Lan, MW… props.

So check out the game and let me know what you think. By making heavy use of Pixie, especially in time limited competitions, I hope to really iron out the core usage scenarios and make it amazing.

The future is bright and full of meaning.

2hr HTML5 Tetris

I’ve taken an interest in limited time game competitions recently and figured I’d better practice up. So as an exercise I tried to make Tetris in 1hr in Pixie. Except for the blocks getting stuck in the walls and that one thing about the lines, it was pretty good for an hour.

I decided that I got pretty close to a “working” game and forked the app to do the 2hr version. I learned that some beveled block graphics really make a difference! As well as actually removing completed lines. There is one bug where sometimes blocks overlap or won’t move into what appears to be empty space, as well as the “choice” of rotation points, but hey, times up.

New Pixie Color Palette

On Pixie we recently redesigned our color palette.

It’s important to choose a good default color palette for your application. For most people the color palette is one of their first impressions of your product, and the palette will largely determine the kinds of things they can make. In addition less than half the people who interact with your product for the first time will even use a custom color, or a different tool. That makes the default choice doubly important.

Let’s say you were stranded on a desert island and only got to take 10 crayons, what would you take? Answering that question is very similar to answering the default palette question.

Since prehistoric times people have enjoyed drawing what they see in their environment. I have a friend, and he wears a blue shirt, so I need the color Shirt Blue. Sometimes he eats apples; I’d better also have Apple Red. The apple came from an apple tree, good thing there’s Leaf Green, and Tree Brown.

Modularity and context are also important. Each of these colors shouldn’t only be a uni-tasker. Shirt Blue can double as Lake Blue when drawn on the ground. Hair Blonde or Hay-bale Yellow? Same color, but it depends on the context. Remember, you’re trapped on an island and need to make these colors count.

The classic defaults are pretty weak. If they had crayola names they’d be something like Maximum Red, Maximum Green, Maximum Blue, Horrendo Cyan, Staring into the Sun Yellow and Good God Man Make it Stop Magenta.

Maybe your friend eats GMO Apples (for maximum redness), as well as wearing outrageous clothing. Mine usually don’t, that’s why I developed the Best Friend’s Apple / Desert Island default color palette.

New Version Of Pixie

Check out the recently released update of Pixie: The Online Pixel Editor. Still here and not checking it out yet? Then let me hit you with the new feature checklist:

  • Undo/Redo
  • Layers
  • Positioning with arrow keys (up, down, left, right)
  • Clone Stamp (shift click to choose clone source)
  • Larger Brush
  • Extensible Tool Architecture

Check out the code linked code samples if you are interested in creating your own tools. I don’t have a full tutorial present, but there are big secret plans in the works. Also, you’ll probably need the source if you want to develop your own tools; get it from github.

Pixie: A jQuery Plugin port of Pixie

That’s right, my idea of a great Saturday afternoon is porting my own code! I wanted to use Pixie in my new Dog Seeder game. It seemed easy enough.

At first I was thinking, “I shouldn’t have to rewrite this”. But then it actually turned out to be unusable. That’s right completely unusable. Well… un-reusable. The first problem was that the code tightly coupled with the specific page html. Referencing certain divs with certain ids. If I wanted to embed it into a new page I’d need to copy all that HTML over. Related to those specific divs the editor was all over the global namespace. There was one `canvas` object and that’s THE canvas, and THE canvas is div#canvas. Ouch…

This was also problematic for extending functionality and adding tools. Each tool was hardcoded into the page. The tools were in div#toolbox and had hardcoded onclick events to call things like `canvas.setTool(pencil)`. More globals… So the mission of extracting that to be useful in another page was too great. It was easier to just rewrite it all with all these good practices in mind, embedability, and extensibility. The long and short of it is that you, gentle reader, really lucked out because now Pixie is way, way better.

Let’s take a look at what’s needed to create a Pixie pixel editor on a new page:

$('#pixie').pixie();

And #pixie is just an empty div! Ok, you do need to include a couple javascript files and some image directories if you want it to work or look good, but nothing special in the HTML itself.

But what if I want to make a new save button that saves the image into Dog Seeder instead locally. I’d need to dig deep into the internals in the old Pixie, but not anymore!

    $("#pixie").pixie({
      initializer: function(canvas) {
        canvas.addAction({
          name: "Save to Creature",
          perform: function(canvas) {
            images[$('#img_path').val()] = canvas.toDataURL();
            $.each(gameObjects, function() {
              $(this).trigger('changed');
            });
          }
        });
      }
    });

Wow, that sure was easy. Maybe I can challenge myself by adding a new tool instead:

          $("#pixie").pixie({
            initializer: function(canvas) {
              var partyPaint = function() {
                this.color(this.canvas.color(rand(2) === 0));
 
                $.each(this.canvas.getNeighbors(this.x, this.y), function(i, neighbor) {
                  if(neighbor) {
                    neighbor.color(neighbor.canvas.color(rand(2) === 0));
                  }
                });
              }
 
              canvas.addTool({
                name: "Party Brush",
                hotkeys: ['Y'],
                mousedown: partyPaint,
                mouseenter: partyPaint
              });
            }
          });

That was pretty easy too. Just to save everyone the shame of asking: “Yes, it’s all easy. Yes you can add as many tools and actions as you want. What, icons? Yes, that’s easy too.” Don’t worry, I’ll post some all the examples I have at the end of the post.

And don’t worry about having to muck with the internals, all the standard tools are written using the same API, so anything the pencil tool or clone stamp tool does you can do too. And a lot more. The opportunities are endless.

Speaking of the API, although I feel that it is pretty good, there may be room for improvement. This is a new release and there may be changes in the future. What I’m trying to say is don’t be afraid to suggest improvements and also don’t be surprised if the API changes in a future version.

So what are some of the amazing possibilities for tool extensions? Let’s take a look at what exists so far:

  var tools = {
    pencil: {
      name: "Pencil",
      hotkeys: ['P'],
      icon: imageDir + "pencil.png",
      cursor: "url(" + imageDir + "pencil.png) 4 14, default",
      mousedown: function(e, color) {
        this.color(color);
      },
      mouseenter: function(e, color) {
        this.color(color);
      }
    },
 
    brush: {
      name: "Brush",
      hotkeys: ['B'],
      icon: imageDir + "paintbrush.png",
      cursor: "url(" + imageDir + "paintbrush.png) 4 14, default",
      mousedown: function(e, color) {
        this.color(color);
 
        $.each(this.canvas.getNeighbors(this.x, this.y), function(i, neighbor) {
          if(neighbor) {
            neighbor.color(color);
          }
        });
      },
      mouseenter: function(e, color) {
        this.color(color);
 
        $.each(this.canvas.getNeighbors(this.x, this.y), function(i, neighbor) {
          if(neighbor) {
            neighbor.color(color);
          }
        });
      }
    },
 
    dropper: {
      name: "Dropper",
      hotkeys: ['I'],
      icon: imageDir + "dropper.png",
      cursor: "url(" + imageDir + "dropper.png) 13 13, default",
      mousedown: function() {
        this.canvas.color(this.color());
        this.canvas.setTool(tools.pencil);
      }
    },
 
    eraser: {
      name: "Eraser",
      hotkeys: ['E'],
      icon: imageDir + "eraser.png",
      cursor: "url(" + imageDir + "eraser.png) 4 11, default",
      mousedown: function() {
        this.color(null);
      },
      mouseenter: function() {
        this.color(null);
      }
    },
 
    fill: {
      name: "Fill",
      hotkeys: ['F'],
      icon: imageDir + "fill.png",
      cursor: "url(" + imageDir + "fill.png) 12 13, default",
      mousedown: function(e, newColor, pixel) {
        // Store original pixel's color here
        var originalColor = this.color();
 
        // Return if original color is same as currentColor
        if(newColor === originalColor) {
          return;
        }
 
        var q = new Array();
        pixel.color(newColor);
        q.push(pixel);
 
        while(q.length > 0) {
          pixel = q.pop();
 
          // Add neighboring pixels to the queue
          var neighbors = this.canvas.getNeighbors(pixel.x, pixel.y);
 
          $.each(neighbors, function(index, neighbor) {
            if(neighbor && neighbor.css("backgroundColor") === originalColor) {
              neighbor.color(newColor);
              q.push(neighbor);
            }
          });
        }
      }
    }
  };

Most of these are pretty simple. Also notice how easy it is to set a hotkey and icon/cursor. The mousedown and mouseenter are the standard tool workhorse methods. They are called with `this` bound to the pixel that the event occurred in. The pixel object is an extended jQuery object, so methods like this.css(someprop, somevalue) also work. The extended part provides utilities like `x`, `y` and `canvas` properties, as well as a color() getter/setter shortcut. Take a look and if it’s not immediately obvious leave a comment so that I can fix my failure and make it clear.

We can also rock a closure to add advanced tools like a clone tool:

  var CloneTool = function() {
    var cloneX, cloneY, targetX, targetY;
 
    return {
      name: "Clone",
      hotkeys: ['C'],
      icon: imageDir + "clone.png",
      cursor: "url("+ imageDir +"clone.png) 0 0, default",
      mousedown: function(e) {
        if(e.shiftKey) {
          cloneX = this.x;
          cloneY = this.y;
        } else {
          targetX = this.x;
          targetY = this.y;
          var selection = this.canvas.getPixel(cloneX, cloneY);
 
          if(selection) {
            this.color(selection.color());
          }
        }
      },
      mouseenter: function(e) {
        var deltaX = this.x - targetX;
        var deltaY = this.y - targetY;
        var selection = this.canvas.getPixel(cloneX + deltaX, cloneY + deltaY);
 
        if(selection) {
          this.color(selection.color());
        }
      }
    };
  };
 
  tools.clone = CloneTool();

The closure keeps track of the state of the tool with shared variables. This allows the source position to be set when shift is held and the mouse is clicked. Then remembered when the tool is used again at a distant location. Ideally we would also set some sort of marker on the source pixel so we can assist the user to see where we are cloning from. This can be done with setting some CSS properties on the pixels, but I will leave that for another day.

Let’s wrap this up with a heads up: there is going to be a contest to create the best tool for Pixie coming up, so it’s never too early to start preparing. The winning tools may even appear in the next version.

So until next time, stay pixelated.

Source on github.