Get Started

What & Why

p5.Riso.js is a p5.js library for generating files suitable for Risograph printing. It helps turn your sketches into multi-color prints.

A Risograph is sort of like a photocopier combined with a screen printer. Just like in screen printing, it prints one color at a time, each as a separate layer. To prepare digital files for riso printing, images need to be separated into individual colors, each saved out as a file. The p5.Riso Library helps automate color separation and lets you preview of how your prints will look. It makes it possible to turn your interactive p5 sketches into print art!

p5.js itself is a beginner-friendly JavaScript library for creative coding. It's based on the metaphor of a sketch, with the browser page being your whole canvas. If you're already familiar with the riso process, you can use p5.riso to preview how your prints might turn out.

Full source and examples on GitHub.

Installation

Local

  • Download the p5.riso.js library file and save it to your sketch folder. Add the path to the library file inside the 'head' tags of your index.html file:
  • <script src="p5.riso.js"></script>

OpenProcessing

  • Create a new tab by clicking the '+' button next to the mySketch tab. Copy & paste the code from the p5.riso.js file straight into your new tab. You can also fork one of our sample sketches (made on OpenProcessing) and work from there!

p5.js Web Editor

  • Click the grey '>' button under the pink play button on the top left to open the file console. Add a new file to your sketch by clicking the '▼' button. We suggest calling it p5.riso.js. Copy & paste the code from the p5.riso.js file into your new file. Then, click on the index.html file on the sidebar. Add the line of code below under the 'head':
  • <script src="p5.riso.js"></script>

Tutorials

Examples

Click to see the source code.

Example and print by Robin Sloan

See additional examples here. Let us know if you make prints with p5.riso and we will add your work to the gallery.

Reference

new Riso()
Riso.fill()
Riso.stroke()
risoNoFill()
risoNoStroke()
clearRiso()
Riso.image()
Riso.cutout()
extractRGBChannel()
extractCMYKChannel()
ditherImage()
drawRiso()
exportRiso()
halftoneImage()


new Riso(color, width, height)

The base Riso object is a single-color p5 graphics object. Its constructor can take 3 parameters:

Internally, the riso object extends p5's built in graphics object. You use it mostly the same way with a few exceptions, as detailed in this documentation.


          let blueChannel; //declare riso object

          function setup(){
            createCanvas(500, 500);
            blueChannel = new Riso("blue"); // instantiate riso object as blue layer
            blueChannel.fill(128); //set opacity
            blueChannel.rect(20, 20, 100, 100); //draw rect to blue layer
            drawRiso(); //draw to screen
          }
        

Here we create a blue color layer, set its fill to be 128 (or half opacity) and then draw a rectangle in it.

Pay attention to the order that you create each Riso object (each color layer). The drawRiso() function that previews your sketch will display the layers in the order they are created, with the last created layer on top. Note that if your top layer has an opacity of 255, you may not be able to see the layers underneath. Different color combination may result in different blended colors.

The following default colors are available:

Riso.fill(value);

The fill method for Riso objects takes a single number value between 0 and 255. 0 is completely transparent, and 255 is completely opaque.


          function setup(){
            createCanvas(500, 500);
            let blueChannel = new Riso("blue"); //create riso object, set to blue

            blueChannel.fill(255); // completely opaque
            blueChannel.ellipse(0, 0, 100, 100); //draw ellipse on blue layer

            blueChannel.fill(50); // fairly transparent
            blueChannel.ellipse(50, 50, 100, 100);

            blueChannel.fill(3); // almost invisible
            blueChannel.ellipse(75, 75, 100, 100);

            drawRiso();
          }
        

Riso.stroke(value);

Just like fill, the stroke method for Riso objects takes a single number value between 0 and 255.


          function setup(){
            createCanvas(500, 500);

            let orange = new Riso("orange");
            orange.stroke(255);
            orange.strokeWeight(5);
            orange.rect(50, 50, 100, 100);
            drawRiso();
          }
        

risoNoFill();

Convenience function that calls noFill() on all riso layers.

risoNoFill();

risoNoStroke();

Convenience function that calls noStroke() on all riso layers.

risoNoStroke();

clearRiso();

Completely clears all riso layers. Useful if you are drawing to riso layers in the draw loop rather than just in setup.

clearRiso();

Riso.image(img, x, y, width, height);

Draws an image onto a riso layer. Acts just like p5's image function, but converts the image to greyscale. It will then print in the color of the layer it is drawn on. Use fill to set the opacity of the image.


          let img;

          function preload() {
            img = loadImage("cat.jpg");
          }

          function setup(){
            createCanvas(500, 500);

            let orange = new Riso("orange");
            orange.fill(200);
            orange.image(img, 0, 0);
            drawRiso();
          }
        

Riso.cutout(p5.Graphic);

This function is the inversion of the mask function. It removes (or masks) portions of one layer that overlap with another layer or graphics object. This is useful for printing in layers when you don't want overlapping colors.


          function setup(){
            createCanvas(500, 500);

            let blue = new Riso("blue"); //blue layer
            let red = new Riso("red"); //red layer

            blue.fill(255); 
            blue.ellipse(130, 130, 100, 100);
            red.fill(255); 
            red.textSize(100);
            red.text("hello",50, 170);

            //completely removes any intersection of red and blue
             //layers from the blue layer
            blue.cutout(red);
            drawRiso();
            }
        

extractRGBChannel(img, channel);

Extracts a red, blue or green color channels from an image, and saves as a new image. Takes in two parameters:


          // convert a color image into a red and blue print
          let img;

          function preload() {
            img = loadImage('cat.jpg')
          }

          function setup(){
            createCanvas(500, 500);

            let blue = new Riso("blue");
            let red = new Riso("red");

            let justBlues = extractRGBChannel(img, "blue"); //extract blue from img and save as new image
            let justReds = extractRGBChannel(img, "red"); //extract red from img and save as new image

            blue.image(justBlues, 0, 0); //draw justblues to blue layer
            red.image(justReds, 0, 0); //draw justred to red layer

            drawRiso();
          }
        

extractCMYKChannel(img, channel);

Extracts the cyan, magneta, yellow and/or black color channels from an image, and saves as a new image. Takes in two parameters:


          // convert a color image into a cyan and magenta print
          let img;

          function preload() {
            img = loadImage('cat.jpg')
          }

          function setup(){
            createCanvas(500, 500);

            let blue = new Riso("blue");
            let red = new Riso("red");

            let justCyan = extractCMYKChannel(img, "cyan"); //extract cyan from img
            let justMagenta = extractCMYKChannel(img, "magenta"); //extract magenta from img

            blue.image(justCyan, 0, 0); //draw justCyan to blue layer
            red.image(justMagenta, 0, 0); //draw justMagenta to red layer

            drawRiso();
          }
        

ditherImage(img, type, threshold);

Reduces an image to one color using patterns of dots to create greys.

Takes three parameters. The first is the image object. The second is the dither type (choose between atkinson, floydsteinberg, bayer or none). The third sets the threshold (it only applies to bayer and none). Dither type 'none' simply applies a threshold, turning pixels above and below the threshold clear and black.


              let black;
              let img;
              let ditherType = 'atkinson';

              function preload() {
                img = loadImage('data/no_threat.jpg');
              }

              function setup() {
                pixelDensity(1);
                createCanvas(img.width, img.height);
                black = new Riso('black'); //create black layer
              }

              function draw() {
                background(220);
                let threshold = map(mouseX, 0, width, 0, 255); //change dither threshold with mouse position
                clearRiso();

                let dithered = ditherImage(img, ditherType, threshold);//pass image to dither
                black.image(dithered, 0, 0); //draw dithered image

                drawRiso();
              }

              function keyReleased() { //function to change dither type with a keypress
                if (key == 1) ditherType = 'atkinson';
                else if (key == 2) ditherType = 'floydsteinberg';
                else if (key == 3) ditherType = 'bayer';
                else if (key == 4) ditherType = 'none';
              }

              

drawRiso();

Draws all Riso objects/layers to the screen. Should be used at the end of your draw or setup functions. Useful for previewing what your prints will look like.


          function setup(){
            createCanvas(500, 500);

            let colors = ["red", "blue", "green", "black"];
            for (let i = 0; i < colors.length; i++) {
              let channel = new Riso(colors[i]); //create four color channels from array
              channel.fill(random(100, 200));
              channel.ellipse(random(0, width), random(0, height), 100, 100); //put a random ellipse on each
            }

            drawRiso(); //preview on screen
          }
        

exportRiso();

Use this function when you are ready to print. It exports all riso layers as individual png files, that can be printed in different ink colors. You should call this function in an event listener like mousePressed, or at the end of your setup function.


          let blue;
          let red;

          function setup(){
            createCanvas(500, 500);

            blue = new Riso("blue");
            red = new Riso("red");
          }

          function draw() {
            clearRiso(); //clear screen every loop of draw

            blue.fill(map(mouseX, 0, width, 0, 255)); //set blue fill by mouse position
            red.fill(map(mouseY, 0, height, 0, 255)); //set red fill by mouse position

            blue.ellipse(200, 200, 100, 100); //draw ellipse to blue layer
            red.ellipse(250, 250, 100, 100); //draw ellipse to red layer
          }

          function mousePressed() { //when mouse is pressed
            exportRiso(); //export red and blue layers as pngs for printing
          }
        

halftoneImage();

Applies a halftone pattern to an image. Takes up to five parameters. The first and only required parameter is the image object. The second is the shape of the dots (default value is a circle). The third is the frequency of dots, measured in lpi (lines per inch), with a default value of 10. The fourth one is the angle to rotate overlapping patterns to reduce interference (default value 45). The fifth parameter is intensity, which will dither the result at the given threshold (default value 127).


              let pink;
              let img;

              function preload() {
                img = loadImage('data/monkeys.jpg');
              }

              function setup() {
                pixelDensity(1);
                createCanvas(img.width, img.height);
                pink = new Riso('fluorescentpink'); // create pink layer
              }

              function draw() {
                background(220);
                clearRiso();

                let halftoned = halftoneImage(img, 'line', 3, 45, 90); // pass image to halftone with line dots, frequency of 3, angle of 45, and intensity of 90.
                pink.image(halftoned, 0, 0); // draw halftoned image

                drawRiso();
              }
        

Troubleshooting

Getting started

This library will only work with the latest version of the p5.js library (release 0.9.0 onwards).

Images don't show up

Running riso.p5 locally requires you to run a local web server to work with image files. If you try to locally run any of the examples with images (such as Dither), it will show the Loading... screen forever. Many text editors (like Sublime Text, Brackets, and VS Code) have plugins to run a server. You can also use a browser app like Web Server for Chrome. A third option is to use the p5.js web editor, which will let you upload image files if you log in.

Image sizes

Set the pixel density of your sketches to 1, using the pixelDensity function in setup(). Else p5.riso will export your images at the default pixel density of your screen (for retina screens, this results in images coming out twice as large as what is set in CreateCanvas). With a pixel density of 1, the exported images will be sized with resolution of 72 dpi. Therefore an image of 600 pixels, will come out to be 600/72 = 8.3 inches wide. If you want to print your image at a higher resolution but smaller physical size, eg. at a dpi of 300, then you need to resize it using photoshop or a tool like this one.

Credits

p5.Riso was created by Sam Lavigne and Tega Brain with web design and contributions from Crystal Chen and Paolla Bruno Dutra. Made with the support of the Integrated Digital Media Program, NYU. Thanks for getting the printer :)

Full source and examples on Github: https://github.com/antiboredom/p5.riso