Animate sprites in R with {pixeltrix}

Gif of Mario walkcycle.

tl;dr

I’ve updated the {pixeltrix} package so you can create animated sprite gifs with a simple, interactive pixel editor from within R’s plot window.

Pix all the right boxes

The {pixeltrix} package—which I’ve written about before—lets you open an interactive R plot that you can click to turn ‘pixels’ on and off.

I created it for one purpose: to quickly create simple, blocky sprites for my {tamRgo} package, which lets you keep a persistent cyberpet on your computer (yes, really).

But wouldn’t it be nice if {pixeltrix} were more… general? Read on for a couple of improvements to the package that might help.

Update

The package has been updated again since this post. From version 0.2 you:

  • can provide colours as input to click_pixels() and frame_pixels()
  • receive a colours attribute with the output matrices, which encodes the state and colour values

Pixellate to accumulate

First, you can install the updated package from GitHub:

remotes::install_github("matt-dray/pixeltrix")  # v0.1.2 in this post
library(pixeltrix)

Now the improvements: plotting with colour, and creating gif animations.

1. Plot

The click_pixel() function opens an interactive plot. If n_state = 3, for example, then each pixel will cycle through three states as you keep clicking it. You’re returned a matrix of these values when you hit Esc.

That was enough for {tamRgo}: I turned a binary matrix into a 1-bit sprite. But wouldn’t it be good—fundamental!—to be able to plot the matrix as an image with user-specified colours? So I made draw_pixels().

I’ve added a three-state matrix, blue, into the package as an example dataset. Let’s plot it with simple colours:

draw_pixels(
  m = pixeltrix::blue, 
  colours = c("white", "#879afb", "gray20")
)

A 14 by 16 pixel grid with a sprite of the main character from the first generation of Pokemon games for the Game Boy. The background is white, the outlines are dark grey and the highlights are light blue.

Of course, it’s the subtly-coloured player character from Pokémon Blue (1996) as seen on the Nintendo Game Boy Color.

2. Animate

Naturally, you could use click_pixels() and draw_pixels() to generate several images and combine them as ‘frames’ of an animation. Why not have a function that does this automatically?

So that’s what I did:

  • frame_pixels() calls click_pixels() and adds the output as the first element of a list, then it passes that matrix into edit_pixels() as the template for the next frame (and so on until you choose to stop making frames)
  • gif_pixels() takes the list of matrices created by frame_pixels() and draws, combines and writes them to a gif

I’ve prepared pixeltrix::mario as an example of an output from frame_pixels(). It contains each of three frames that comprise Mario’s walk cycle from Super Mario Bros on the NES.

Here’s what the console output looked like when I made mario:

mario <- frame_pixels(
  n_rows   = 16,
  n_cols   = 16,
  n_states = 4  # background + 3 colours
)
# Click squares in the plot window. Press <Esc> to end.
# Add a frame? y/n: y
# Click squares in the plot window. Press <Esc> to end.
# Current frame count: 2
# Add a frame? y/n: y
# Click squares in the plot window. Press <Esc> to end.
# Current frame count: 3
# Add a frame? y/n: n
# Final frame count: 3

You can see there’s interactivity; the user is prompted to add another frame with Add a frame? y/n:, where y will let you create a new frame and n will stop the process and return the list of matrices.

And so you can see it’s a list of three matrices:

str(mario)
## List of 3
##  $ : int [1:16, 1:16] 0 0 0 0 0 0 0 0 1 1 ...
##  $ : int [1:16, 1:16] 0 0 0 0 0 0 0 0 0 0 ...
##  $ : int [1:16, 1:16] 0 0 0 0 0 0 0 0 0 0 ...

You can then convert the list to a gif with gif_pixels(), which engifs the frames using {gifski}.1

gif_pixels(
  frames = mario,
  colours = c(
    "white",    # background
    "#FDA428",  # skin (yellowish)
    "#FC0D1B",  # overalls/hat (red)
    "#A32B2E"   # hair, eyes, shirt, boots (brown)
  ),
  file = "mario.gif",
  delay = 0.15  # passed via dots to gifski::save_gif()
)
# Inserting image 3 at 0.30s (100%)...
# Encoding to gif... done!
# [1] "mario.gif"

And if we open that file:

An animated 16 by 16 pixel grid with a coloured sprite of Mario from the original Super Mario Bros for the NES. There are three frames that each show a step in Mario's walk cycle.

Yahoooooo, created entirely with R. Noice.

Pix n mix

So {pixeltrix} finally got a couple of nice-to-have (well, must-have) functions. This is enough for me to continue just messing around with it as a novelty2.

I mean, come on: animated pixelart created in an interactive R plot window? Why? I mean, er… wow!


Session info
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value
##  version  R version 4.2.0 (2022-04-22)
##  os       macOS Big Sur/Monterey 10.16
##  system   x86_64, darwin17.0
##  ui       X11
##  language (EN)
##  collate  en_US.UTF-8
##  ctype    en_US.UTF-8
##  tz       Europe/London
##  date     2022-12-22
##  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package     * version date (UTC) lib source
##  blogdown      1.9     2022-03-28 [1] CRAN (R 4.2.0)
##  bookdown      0.26    2022-04-15 [1] CRAN (R 4.2.0)
##  bslib         0.3.1   2021-10-06 [1] CRAN (R 4.2.0)
##  cli           3.3.0   2022-04-25 [1] CRAN (R 4.2.0)
##  digest        0.6.29  2021-12-01 [1] CRAN (R 4.2.0)
##  evaluate      0.15    2022-02-18 [1] CRAN (R 4.2.0)
##  fastmap       1.1.0   2021-01-25 [1] CRAN (R 4.2.0)
##  fontawesome   0.2.2   2021-07-02 [1] CRAN (R 4.2.0)
##  htmltools     0.5.2   2021-08-25 [1] CRAN (R 4.2.0)
##  jquerylib     0.1.4   2021-04-26 [1] CRAN (R 4.2.0)
##  jsonlite      1.8.0   2022-02-22 [1] CRAN (R 4.2.0)
##  knitr         1.39    2022-04-26 [1] CRAN (R 4.2.0)
##  magrittr      2.0.3   2022-03-30 [1] CRAN (R 4.2.0)
##  pixeltrix   * 0.2.0   2022-12-22 [1] local
##  R6            2.5.1   2021-08-19 [1] CRAN (R 4.2.0)
##  rlang         1.0.2   2022-03-04 [1] CRAN (R 4.2.0)
##  rmarkdown     2.14    2022-04-25 [1] CRAN (R 4.2.0)
##  rstudioapi    0.14    2022-08-22 [1] CRAN (R 4.2.0)
##  sass          0.4.1   2022-03-23 [1] CRAN (R 4.2.0)
##  sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.2.0)
##  stringi       1.7.6   2021-11-29 [1] CRAN (R 4.2.0)
##  stringr       1.4.0   2019-02-10 [1] CRAN (R 4.2.0)
##  xfun          0.30    2022-03-02 [1] CRAN (R 4.2.0)
##  yaml          2.3.5   2022-02-21 [1] CRAN (R 4.2.0)
## 
##  [1] /Library/Frameworks/R.framework/Versions/4.2/Resources/library
## 
## ──────────────────────────────────────────────────────────────────────────────

  1. The {pixeltrix} package has no dependencies and I didn’t want to force a user to install {gifski} if they weren’t going to use gif_pixels(). It’s therefore up to the user to install it. I also wonder if I should provide an argument for the user to name a ‘gif engine’ of choice, e.g. {gifski} or {magick}, depending on what they’ve got installed on their machine.↩︎

  2. It’s never, ever going to have the features and quality of a premium pixel-art program like Aseprite, obviously.↩︎