HTML5 Canvases & Transforms

Browsers supporting HTML5 allow you to draw on the browser’s screen without preparing an image file before. Drawing on a canvas is done using the wonderful Javascript language. If you want to draw a 2-dimensional image on canvas element ‘cnv’, get the drawing context using:

var ctx = cnv.getContext("2d")

And use that context to draw everything using the standard functions:

moveTo, lineTo, arc, fillRect, etc.

Learn more about drawing here.

You can use the functions to create more complicated shapes easily thanks to transform functions:

Transformations: Linear Algebra Made Easy

The origin(0,0) of the canvas is defined to be the top-left pixel of the canvas element. And the coordinates are given in number of pixels. This can change by using transforms.

The transformation functions are:

  • scale(x,y): this will make every shape x times wider and y time taller.
  • translate(x,y): now coordinate (0,0) will be shifted x units to the right, and y units down.
  • rotate(angle): will rotete the axes by given angle in radians.
  • transform(a,b,c,d,e,f): If you want to use a transformation matrix
  • setTransfrom(a,b,c,d,e,f): reset all transform, and perform transform(a,b,c,d,e,f).

a,b,c,d,e,f are values in the matrix:

Transform Matrix
The values a,b,c,d are used for rotating and scaling. e,f for translating.

Other useful methods of the context are:

  • – to save current transform in a stack (Last In First Out).
  • ctx.restore() – to retrieve the trnasform from the top of the stack.

An Example – The Koch Snowflake

The algorithm for drawing the Koch Snowflake can be found in the post Drawing The Koch Snowflake Fractal With GIMP.

Here’s an example in Javascript:

        function drawSide(ctx, len){
          if (len > 1) {
            var thirdOfLen = len / 3.;

            var rotationAngles = [0, -Math.PI / 3, 2 * Math.PI / 3., -Math.PI / 3];

              if (val != 0){
                ctx.translate(thirdOfLen, 0);
              drawSide(ctx, thirdOfLen);

          } else {

        ctx.translate(startX, startY);
        for (i=0; i<3; i++){
          drawSide(ctx, sideLength);

Warning: using ctx.stroke() after every little line you draw might make your code inefficient, and slow down your browser.

Transformation functions are also part of the Processing language.


Drawing The Koch Snowflake Fractal With GIMP

I once found myself discussing in some forums about conversion of many pictures from one format to another. I knew that many image processors can work in batch mode. The first image processor I thought of was GIMP. As expected, you can get help on this tool when run from the command-line with the option ‘–help’. In linux you can use ‘man gimp” to get more details about the tool. I found that ‘-b’ is the option to use to run GIMP in batch mode, and that the default scripting language is Script-Fu. Now, what is Script-Fu?  From the menu bar: “Help -> GIMP-Online -> User Manual Web Site” leads to the following page:

Now, choose your language and version, and go to the chapter about scripting. Choose section ‘Using Script-Fu Scripts’. This language is based on Scheme, a language very similar to list. To work with Script Fu, choose ‘from the menu bar “Filters->Script-Fu”.

To load an image, use the function ‘gimp-file-load’.

Ti save a file, use the function ‘gimp-file-save’

.Well, this is not the tool I would use to convert image files.

I decided to challenge myself in drawing the Koch-Snowflake  in Script-Fu. The following picture is what the fractal looks like:


Now, how to draw this fractal? Simply draw an equilateral triangle, and follow the following steps until you no longer have a polygon:

  1.  Split each side of the polygon into three sections equal in length.
  2. Replace the middle section by two sides of an equilateral triangle, pointing outwards.

Now, how to do it with Script-Fu?

Copy the draw-line function from here.

define a function to draw the fractal, with an inner function to draw a side (origianlly, a side of a triangle).

This is the code:

(define (sqr x) (* x x))

(define (draw-line layer x-from y-from x-to y-to) 
  (let* (
          (points (cons-array 4 'double))

        (aset points 0 x-from)
        (aset points 1 y-from)
        (aset points 2 x-to  )
        (aset points 3 y-to  )

        (gimp-pencil layer 4 points)

(define (draw-koch-snowflake layer height width)
  (let* (
      ; sin60, cos60 will be used for rotating lines 60 degrees
      (sin60 (/ (sqrt 3) 2)) ; sqrt(3)/2 - No operator precedence in LISP/Scheme
      (cos60 0.5)

      (min-size (min height width))
      (side-length (* sin60 min-size))

    (define (draw-side layer from-x from-y to-x to-y)
      (let* (
           (diff-x (- to-x from-x))
           (diff-y (- to-y from-y))
           (third-diff-x (/ diff-x 3))
           (third-diff-y (/ diff-y 3))
           (squared-length (+ (sqr diff-x) (sqr diff-y)))
       (if (<= squared-length 1)                    ;; The (squared) distance is less than or equal to 1 pixel?
          (draw-line layer from-x from-y to-x to-y) ;; Yes, just draw the line
          (begin                                    ;; No, recursively split the side and draw it.
            ;; recursively draw the 1st section
             (define end-section-1-x (+ from-x third-diff-x))
             (define end-section-1-y (+ from-y third-diff-y))
             (draw-side layer from-x from-y end-section-1-x end-section-1-y)

             ;; create a new vertex, the 2nd section is replaced by 2 sides
             ;; of an equilateral triangle
             ;; This is how you rotate a vector 60 degrees counterclockwise. 
             (define new-vertex-x
               (+ end-section-1-x (* cos60 third-diff-x) (* sin60 third-diff-y))
             ); define new-vertex-y
             (define new-vertex-y
               (+ end-section-1-y (* (- sin60) third-diff-x) (* cos60 third-diff-y))
             ); define new-vertex-y
             (draw-side layer end-section-1-x end-section-1-y new-vertex-x new-vertex-y)

             ;; recursively draw the last section
             ;; and the section from the new vertex to the beginning of
             ;; the last section.
             (define start-section-3x (- to-x third-diff-x))
             (define start-section-3y (- to-y third-diff-y))
             (draw-side layer new-vertex-x new-vertex-y start-section-3x start-section-3y)
             (draw-side layer start-section-3x start-section-3y to-x to-y)
          ) ; End of command block
      );let* in draw-side
    ); define draw-side
    (define first-vertex-x (/ (- width side-length) 2))
    (define first-vertex-y (- (- height (* side-length sin60)) 
                              (/ (- height min-size) 2)))
    (define start-x first-vertex-x)
    (define start-y first-vertex-y)
    (define end-x (+ start-x side-length))
    (define end-y start-y)
    (draw-side layer start-x start-y end-x end-y)

    (set! start-x end-x)
    (set! start-y end-y)
    (set! end-x (- end-x (* cos60 side-length)))
    (set! end-y (+ end-y (* sin60 side-length)))
    (draw-side layer start-x start-y end-x end-y)

    (set! start-x end-x)
    (set! start-y end-y)
    (set! end-x first-vertex-x)
    (set! end-y first-vertex-y)
    (draw-side layer start-x start-y end-x end-y)

  );  let* in draw-koch-snowflake

; The caller function:

(define (koch-caller)
     (my-image (car (gimp-image-new 600 600 RGB)))
     (my-layer (car (gimp-layer-new my-image 600 600 RGB-IMAGE "my layer" 100 NORMAL))) 
     ;; You add a layer because you cannot draw directly on the image object.
   (gimp-image-add-layer my-image my-layer -1)    ; Add the layer to the image
   (gimp-context-set-background '(255 255 255))   ; White background
   (gimp-context-set-foreground '(000 000 000))   ; Black foreground
   (gimp-context-set-opacity 100)                 ; So you can see the picture
   (gimp-drawable-fill my-layer BACKGROUND-FILL)  ; First, fill the layer with background color.
   (gimp-brushes-set-brush "Circle (01)")         ; The thinnest line.
   (draw-koch-snowflake my-layer 600 600)         ; Draw a 600 X 600 snowflake
   (gimp-display-new my-image)                    ; Physically display the result

Now, to better understand how to rotate a vector, refer to this Wikipedia entry.