├── index.html ├── multi.html ├── mixture.html ├── predict.html ├── temperature.html ├── multi_svg.html ├── predict_svg.html ├── README.md ├── sketch_basic.js ├── sketch_temperature.js ├── sketch_mixture.js ├── sketch_predict_svg.js ├── sketch_predict.js ├── sketch_multi_svg.js ├── sketch_multi.js ├── model.js └── p5.svg.js /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /multi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /mixture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /predict.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /temperature.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /multi_svg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /predict_svg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rnn-tutorial 2 | RNN Tutorial for Artists 3 | 4 | ![title_img](https://cdn.rawgit.com/hardmaru/rnn-tutorial/master/neural.svg) 5 | 6 | Suplementary material for [blog post](http://blog.otoro.net/2017/01/01/recurrent-neural-network-artist/). 7 | 8 | ## Usage ## 9 | 10 | In the html file, include the following files: 11 | 12 | ```html 13 | 14 | 15 | 16 | 17 | 18 | ``` 19 | 20 | The sketch in `sketch_basic.js` is a basic example of how the Handwriting Model works. It is written with p5.js as an object hence allowing multiple sketches to be on the same page. 21 | 22 | ## License ## 23 | 24 | MIT 25 | -------------------------------------------------------------------------------- /sketch_basic.js: -------------------------------------------------------------------------------- 1 | // Basic Example of Unconditional Handwriting Generation. 2 | var sketch = function( p ) { 3 | "use strict"; 4 | 5 | // variables we need for this demo 6 | var dx, dy; // offsets of the pen strokes, in pixels 7 | var pen, prev_pen; // keep track of whether pen is touching paper 8 | var x, y; // absolute coordinates on the screen of where the pen is 9 | 10 | var rnn_state; // store the hidden states of rnn's neurons 11 | var pdf; // store all the parameters of a mixture-density distribution 12 | var temperature = 0.65; // controls the amount of uncertainty of the model 13 | 14 | var screen_width, screen_height; // stores the browser's dimensions 15 | var line_color; 16 | 17 | var restart = function() { 18 | // reinitialize variables before calling p5.js setup. 19 | 20 | // make sure we enforce some minimum size of our demo 21 | screen_width = Math.max(window.innerWidth, 480); 22 | screen_height = Math.max(window.innerHeight, 320); 23 | 24 | // start drawing from somewhere in middle of the canvas 25 | x = 50; 26 | y = p.random(100, screen_height-100); 27 | 28 | // initialize the scale factor for the model. Bigger -> large outputs 29 | Model.set_scale_factor(10.0); 30 | 31 | // initialize pen's states to zero. 32 | [dx, dy, prev_pen] = Model.zero_input(); // the pen's states 33 | 34 | // randomize the rnn's initial states 35 | rnn_state = Model.random_state(); 36 | 37 | // define color of line 38 | line_color = p.color(p.random(64, 224), p.random(64, 224), p.random(64, 224)) 39 | 40 | }; 41 | 42 | p.setup = function() { 43 | restart(); // initialize variables for this demo 44 | p.createCanvas(screen_width, screen_height); 45 | p.frameRate(60); 46 | p.background(255, 255, 255, 255); 47 | p.fill(255, 255, 255, 255); 48 | }; 49 | 50 | p.draw = function() { 51 | 52 | // using the previous pen states, and hidden state, get next hidden state 53 | rnn_state = Model.update([dx, dy, prev_pen], rnn_state); 54 | 55 | // get the parameters of the probability distribution (pdf) from hidden state 56 | pdf = Model.get_pdf(rnn_state); 57 | 58 | // sample the next pen's states from our probability distribution 59 | [dx, dy, pen] = Model.sample(pdf, temperature); 60 | 61 | // only draw on the paper if the pen is touching the paper 62 | if (prev_pen == 0) { 63 | p.stroke(line_color); 64 | p.strokeWeight(2.0); 65 | p.line(x, y, x+dx, y+dy); // draw line connecting prev point to current point. 66 | } 67 | 68 | // update the absolute coordinates from the offsets 69 | x += dx; 70 | y += dy; 71 | 72 | // update the previous pen's state to the current one we just sampled 73 | prev_pen = pen; 74 | 75 | // if the rnn starts drawing close to the right side of the canvas, reset demo 76 | if (x > screen_width - 50) { 77 | restart(); 78 | } 79 | 80 | if (p.frameCount % 30 == 0) { 81 | p.background(255, 255, 255, 16); // fade out a bit. 82 | p.fill(255, 255, 255, 32); 83 | } 84 | 85 | }; 86 | 87 | }; 88 | var custom_p5 = new p5(sketch, 'sketch'); 89 | -------------------------------------------------------------------------------- /sketch_temperature.js: -------------------------------------------------------------------------------- 1 | // Basic Example of Unconditional Handwriting Generation. 2 | var sketch = function( p ) { 3 | "use strict"; 4 | 5 | // variables we need for this demo 6 | var dx, dy; // offsets of the pen strokes, in pixels 7 | var pen, prev_pen; // keep track of whether pen is touching paper 8 | var x, y; // absolute coordinates on the screen of where the pen is 9 | 10 | var rnn_state; // store the hidden states of rnn's neurons 11 | var pdf; // store all the parameters of a mixture-density distribution 12 | var temperature = 0.65; // controls the amount of uncertainty of the model 13 | 14 | var screen_width, screen_height; // stores the browser's dimensions 15 | var line_color; 16 | 17 | var restart = function() { 18 | // reinitialize variables before calling p5.js setup. 19 | 20 | // make sure we enforce some minimum size of our demo 21 | screen_width = Math.max(window.innerWidth, 480); 22 | screen_height = Math.max(window.innerHeight, 320); 23 | 24 | // start drawing from somewhere in middle of the canvas 25 | x = 50; 26 | y = screen_height/2; 27 | 28 | // initialize the scale factor for the model. Bigger -> large outputs 29 | Model.set_scale_factor(10.0); 30 | 31 | // initialize pen's states to zero. 32 | [dx, dy, prev_pen] = Model.zero_input(); // the pen's states 33 | 34 | // randomize the rnn's initial states 35 | rnn_state = Model.random_state(); 36 | 37 | // define color of line 38 | line_color = p.color(255, 165, 0); 39 | 40 | }; 41 | 42 | var generate = function() { 43 | 44 | p.noStroke(); 45 | p.fill(255); 46 | p.rect(0, 0, screen_width, screen_height*0.105); 47 | 48 | p.fill(255, 165, 0, 128+127*temperature); 49 | p.rect(0, 0, screen_width*temperature/1.25, screen_height*0.10); 50 | p.textSize(40); 51 | 52 | p.text(Math.round(temperature*100)/100, screen_width*temperature/1.25+15, screen_height*0.10); 53 | 54 | } 55 | 56 | p.setup = function() { 57 | restart(); // initialize variables for this demo 58 | p.createCanvas(screen_width, screen_height); 59 | p.frameRate(60); 60 | p.background(255); 61 | p.fill(255); 62 | generate(); 63 | }; 64 | 65 | p.draw = function() { 66 | 67 | // using the previous pen states, and hidden state, get next hidden state 68 | rnn_state = Model.update([dx, dy, prev_pen], rnn_state); 69 | 70 | // get the parameters of the probability distribution (pdf) from hidden state 71 | pdf = Model.get_pdf(rnn_state); 72 | 73 | // sample the next pen's states from our probability distribution 74 | [dx, dy, pen] = Model.sample(pdf, temperature); 75 | 76 | // only draw on the paper if the pen is touching the paper 77 | if (prev_pen == 0) { 78 | p.stroke(line_color); 79 | p.strokeWeight(2.0); 80 | p.line(x, y, x+dx, y+dy); // draw line connecting prev point to current point. 81 | } 82 | 83 | // update the absolute coordinates from the offsets 84 | x += dx; 85 | y += dy; 86 | 87 | // update the previous pen's state to the current one we just sampled 88 | prev_pen = pen; 89 | 90 | // if the rnn starts drawing close to the right side of the canvas, reset demo 91 | if (x > screen_width - 50) { 92 | restart(); 93 | p.background(255); // fade out a bit. 94 | p.fill(255); 95 | generate(); 96 | } 97 | 98 | }; 99 | 100 | var touched = function() { 101 | var mx = p.mouseX; 102 | if (mx >= 0 && mx < screen_width) { 103 | temperature = 1.25*mx / screen_width; 104 | generate(); 105 | } 106 | }; 107 | 108 | p.touchMoved = touched; 109 | p.touchStarted = touched; 110 | 111 | }; 112 | var custom_p5 = new p5(sketch, 'sketch'); 113 | -------------------------------------------------------------------------------- /sketch_mixture.js: -------------------------------------------------------------------------------- 1 | // Basic Example of Unconditional Handwriting Generation. 2 | var sketch = function( p ) { 3 | "use strict"; 4 | 5 | // variables we need for this demo 6 | 7 | var temperature = 0.65; // controls the amount of uncertainty of the model 8 | var Nmix = 20; 9 | var screen_width; 10 | var screen_height; 11 | 12 | var Nbar = 401; 13 | 14 | var base_pi = new Array(Nmix); 15 | var base_mu = new Array(Nmix); 16 | var base_sigma = new Array(Nmix); 17 | 18 | var pi = new Array(Nmix); 19 | var mu = new Array(Nmix); 20 | var sigma = new Array(Nmix); 21 | 22 | var ybar = new Array(Nbar); 23 | var xbar = new Array(Nbar); 24 | 25 | var factor = 5.0; 26 | 27 | var erf_constant = 1/Math.sqrt(2*Math.PI); 28 | 29 | var gaussian = function(x, mean, std) { 30 | var f = erf_constant / std; 31 | var p = -1/2; 32 | var c = (x-mean)/std; 33 | c *= c; 34 | p *= c; 35 | return f * Math.pow(Math.E, p); 36 | }; 37 | 38 | var create_base_distribution = function() { 39 | var i, pi_sample; 40 | var pi_sum = 0; 41 | for (i=0;i