Getting started

Overview

sketch is an R package for creating animated and interactive visualisations. It lets users develop JavaScript-style visualisations using just the R syntax by transpiling R code into JavaScript code. It is designed to help

  • researchers create domain specific visualisation to support development and sharing of research,
  • journalists and science communicators publish high-quality and engaging interactive graphics,
  • business users make customised in-house reports, and
  • general users learn generative arts and interactive data visualisation.

Installation

# install.packages("remotes")
remotes::install_github("kcf-jackson/sketch")

Running a sketch R file

There are two ways to run a sketch R file.

  1. Save the sketch R file (with the usual .R extension), then call sketch::source_r with it.

  2. If you use RStudio, there is an add-in “source a sketch R file” listed under this package (consider binding that to the key combination alt-shift-s), and that sources the active tab in the editor. Alternatively, you can call the source_active() function.


First example with sketch

As the first example with sketch, we will use p5.js to make this animated visualisation:

  • An important aspect of this package is that “Apps” produced by sketch run in the browser natively without an R back-end. The App is live, and it supports interactivity.

  • In fact, you can write sketch code directly in R Markdown documents, generate an HTML file, and share it online easily (just like this page you are reading right now!).

  • p5.js is picked because it has an user-friendly API and many good learning resources online.


i. Basic structure

The structure of a p5 application looks like this:

#! load_library("p5")

setup <- function() {       
    createCanvas(400, 300)  # create a canvas of size 400 x 300 (w x h)
}

draw <- function() {        
    background(0, 0, 33)    # paint the background with the RGB colour
    fill("red")             # change fill colour to "red"
    circle(200, 150, 50)    # draw a circle (x, y, diameter)
}
  • #! load_library is used to load the p5 library. Despite being commented out, lines starting with #! are actually processed by the sketch package.

  • p5.js looks for functions named (specifically) setup and draw, and it runs

    • setup once at the start of the App, and
    • draw iteratively 60 times per second after the App starts.
  • createCanvas, background, fill and circle are functions provided by p5.js for drawing on the screen.

ii. Add more circles

Let’s create a “person” object, and draw 50 of them on the screen. We need an id to identify the person and its coordinates x and y at which a circle will be drawn.

#! load_library("p5")

# Make a "person" object
person <- function(id) {
    declare(res)    # New variable in a function must be declared before use!
    res <- list(id = id, 
                x = runif(1, 0, 400), y = runif(1, 0, 300))
    return(res)     # Return must be explicit, or it will return "undefined".
}

# Set up variables
radius <- 5
people <- map(1:50, person)

setup <- function() {
    createCanvas(400, 300)  
}

draw <- function() {
    background(0, 0, 33)    
    for (person in people) {   # Use a for loop to draw one person at a time
        fill(190, 128, 0)                
        circle(person$x, person$y, 2*radius)   
    }
}

iii. Add movements

To make the people move, we add two velocity states vx and vy to the “person” object and a function that changes a person’s position based on its velocity.

An important point to notice is that JavaScript* passes object by reference, so when you pass an object to a function and make changes to it, the changes applies to the object directly and no copying occurs. To clarify this point, the super-assign symbol <<- is used below to indicate where one should expect object modification.

(*Did you notice you have been writing JavaScript?)

#! load_library("p5")

# Make a "person" object
person <- function(id) {
    declare(res)    
    res <- list(id = id, 
                x = runif(1, 0, 400), y = runif(1, 0, 300),
                vx = runif(1, -2, 2), vy = runif(1, -2, 2))  # Add velocity
    return(res)
}

move <- function(person) {
    declare (new_x, new_y)            # Do not forget the variable declaration!
    new_x <- person$x + person$vx     # Update position
    new_y <- person$y + person$vy
    
    # If the new position is out of the screen, the person should turn back!
    # Otherwise, move to the new position
    if (new_x < 0 || new_x > 400) { 
        person$vx <<- person$vx * -1  # Turn back!
    } else {
        person$x <<- new_x            # Move to the new position
    }
    
    # Do the same for y
    if (new_y < 0 || new_y > 300) { 
        person$vy <<- person$vy * -1  # Turn back!
    } else {
        person$y <<- new_y            # Move to the new position
    } 
}

# Set up variables
radius <- 5
people <- map(1:50, person)

setup <- function() {
    createCanvas(400, 300)  
}

draw <- function() {
    background(0, 0, 33)    
    for (person in people) {
        fill(190, 128, 0)                
        circle(person$x, person$y, 2*radius)
        move(person)                  # Call `move` here
    }
}

Summary

We have learnt that

  • #! load_library("p5") is used to load the p5.js library,
  • setup and draw forms the basic structure of a p5 canvas,
  • background, fill and circle are drawing functions from p5.js,
  • new variables inside a function must be declared before use, and
  • JavaScript passes object by reference.

Next step

That’s it! You have successfully written a JavaScript App without actually writing any Javascript! I hope you find the examples easy to follow and are convinced that using JavaScript - even to a great extent - does not need to be hard. Leveraging the existing JavaScript libraries, sketch opens up many new possibilities with visualisations. For the next step,

  • see Concepts for the fundamentals of the package,

  • see Features for the other features of the package, or

  • follow the Tutorials to make some interactive visualisations!