diff --git a/.gitignore b/.gitignore
index 04dbeaf084b05b24c1a0072691df60295bfd4cdb..e856af2d8d196df4af2e2146da3319e119282b49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 a.out
 .vscode/*
+book/*
diff --git a/js/bouncing_ball.html b/js/bouncing_ball.html
new file mode 100644
index 0000000000000000000000000000000000000000..be55e022ebf05b48c80a33b42bfa01b2f52343d3
--- /dev/null
+++ b/js/bouncing_ball.html
@@ -0,0 +1,38 @@
+<canvas id="stage"></canvas>
+<script src="graphics.js"></script>
+<script>
+    (async function () {
+        x = g.getmaxx() / 2;
+        y = g.getmaxy() / 2;
+        up = 0;
+        left = 0;
+        while (true) {
+            if (y >= g.getmaxy() - 30)
+                up = 1;
+            if (y <= 30)
+                up = 0;
+
+            if (x >= g.getmaxx() - 30)
+                left = 1;
+            if (x <= 30)
+                left = 0;
+
+            g.circle(x, y, 30);
+
+            /* prepare for next  frame */
+            await g.delay(15);
+            g.cleardevice();
+
+            if (up)
+                y = y - 5;
+            else
+                y = y + 5;
+
+            if (left)
+                x = x - 5;
+            else
+                x = x + 5;
+        }
+    })();
+
+</script>
diff --git a/js/fan.html b/js/fan.html
new file mode 100644
index 0000000000000000000000000000000000000000..4ec8bdfb0f60005a948597602a32e0ae4bd5ccaf
--- /dev/null
+++ b/js/fan.html
@@ -0,0 +1,66 @@
+<canvas id="stage"></canvas>
+<script src="graphics.js"></script>
+<script>
+    function rotatex(x, y, angle, pivotx, pivoty) {
+        return x * Math.cos(angle) - y * Math.sin(angle) + pivotx * (1 - Math.cos(angle)) + pivoty * (Math.sin(angle));
+    }
+    function rotatey(x, y, angle, pivotx, pivoty) {
+        return x * Math.sin(angle) + y * Math.cos(angle) + pivoty * (1 - Math.cos(angle)) - pivotx * (Math.sin(angle));
+    }
+    (async function () {
+        var cx, cy;
+        var l1x1, l1y1, l1x2, l1y2;
+        var l1angle = 120 / 180.0 * Math.PI;
+        var l2angle = 240 / 180.0 * Math.PI;
+        var rotate_angle = 2 / 180.0 * Math.PI;
+        var i= 0;
+
+        cx = g.getmaxx() / 2;
+        cy = g.getmaxy() / 2;
+
+        l1x1 = cx;
+        l1y1 = cy - 20;
+        l1x2 = cx;
+        l1y2 = cy - 90;
+
+        while (true) {
+            i++;
+            if (i >= 60) {
+                l1x1 = cx;
+                l1y1 = cy - 20;
+                l1x2 = cx;
+                l1y2 = cy - 90;
+                i = 0;
+
+            }
+
+            g.circle(cx, cy, 20);
+            g.line(l1x1, l1y1, l1x2,  l1y2);
+
+            g.line(
+                rotatex(l1x1, l1y1, l1angle, cx, cy),
+                rotatey(l1x1, l1y1, l1angle, cx, cy),
+                rotatex(l1x2, l1y2, l1angle, cx, cy),
+                rotatey(l1x2, l1y2, l1angle, cx, cy)
+            );
+
+            g.line(
+                rotatex(l1x1, l1y1, l2angle, cx, cy),
+                rotatey(l1x1, l1y1, l2angle, cx, cy),
+                rotatex(l1x2, l1y2, l2angle, cx, cy),
+                rotatey(l1x2, l1y2, l2angle, cx, cy)
+            );
+
+            /* prepare for next  frame */
+            await g.delay(15);
+            g.cleardevice();
+
+            l1x1 = rotatex(l1x1, l1y1, rotate_angle, cx, cy);
+            l1y1 = rotatey(l1x1, l1y1, rotate_angle, cx, cy);
+            l1x2 = rotatex(l1x2, l1y2, rotate_angle, cx, cy);
+            l1y2 = rotatey(l1x2, l1y2, rotate_angle, cx, cy);
+
+        }
+    })();
+
+</script>
diff --git a/js/graphics.js b/js/graphics.js
new file mode 100644
index 0000000000000000000000000000000000000000..a23f006f428d348b0c04a1ba37683fe9d7540c97
--- /dev/null
+++ b/js/graphics.js
@@ -0,0 +1,41 @@
+document.body.setAttribute("style", "margin: 0;");
+var canvas = document.getElementById('stage');
+
+canvas.width = window.innerWidth
+    || document.documentElement.clientWidth
+    || document.body.clientWidth;
+canvas.height = window.innerHeight
+    || document.documentElement.clientHeight
+    || document.body.clientHeight;
+var g = canvas.getContext("2d");
+
+g.getmaxx = function () { return canvas.width; };
+g.getmaxy = function () { return canvas.height; };
+
+g.putpixel = function (x, y) {
+    g.fillRect(x, y, 1, 1);
+};
+
+g.line = function (x1, y1, x2, y2) {
+    g.beginPath();
+    g.moveTo(x1, y1);
+    g.lineTo(x2, y2);
+    g.stroke();
+};
+
+g.rectangle = function (x1, y1, x2, y2) {
+    g.strokeRect(x1, y1, x2-x1, y2-y1);
+};
+
+g.circle = function (cx, cy, r) {
+    g.beginPath();
+    g.arc(cx, cy, r, 0, 2*Math.PI);
+    g.stroke();
+};
+
+g.cleardevice = function () {
+    g.clearRect(0,0,g.getmaxx(),g.getmaxy());
+};
+g.delay = function (ms) {
+    return new Promise(resolve => setTimeout(resolve, ms));
+  }