We keep information about Mario in a single record type with fields that
represent the current x and y coordinates (x and y), current velocity
(vx and vy) and the current direction dir. The direction is used to
pick the correct Mario image when rendering:
| 1: 
2: 
3: 
4: 
 | type Mario =
  { x:float; y:float;
    vx:float; vy:float;
    dir:string }
 | 
The step function of the game takes previvous Mario value and returns a new
one. It is composed from 4 functions that represent different aspects of the game.
The functions that depend on keyboard take the current keyboard state as the
first argument. This is represented as a tuple int*int consisting of x and y
directions. For example, when the left key is pressed, the value is (-1, 0).
|  1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
 | // If the Up key is pressed (y > 0) and Mario is on the ground,
// then create Mario with the y velocity 'vy' set to 5
let jump (_,y) m =
  if y > 0 && m.y = 0. then { m with vy = 5. } else m
// If Mario is in the air, then his "up" velocity is decreasing
let gravity m =
  if m.y > 0. then { m with vy = m.vy - 0.1 } else m
// Apply physics - move Mario according to the current velocities
let physics m =
  { m with x = m.x + m.vx; y = max 0. (m.y + m.vy) }
// When Left/Right keys are pressed, change 'vx' and direction
let walk (x,_) m =
  let dir = if x < 0 then "left" elif x > 0 then "right" else m.dir
  { m with vx = float x; dir = dir }
 | 
The step function takes a dir parameter representing the keyboard status
and a current Mario state. It simply runs 4 components in a pipeline:
| 1: 
2: 
 | let step dir mario =
  mario |> physics |> walk dir |> gravity |> jump dir
 | 
Now we're ready to render Mario using HTML 5 canvas! To do that, we need the
width and height of the canvas and the current state of Mario. The following
function fills the bottom half of the canvas with green, upper half with blue
and then chooses the right Mario image. It uses helpers from the Win
module, which are discussed below:
|  1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
 | /// Render mario on canvas
let render (w,h) (mario:Mario) =
    // Render background
    (0.,0.,w,h) |> Win.filled (Win.rgb 174 238 238)
    (0.,h-50.,w,50.) |> Win.filled (Win.rgb 74 163 41)
    // Select and position Mario
    // (walking is represented as an animated gif)
    let verb =
        if mario.y > 0. then "jump"
        elif mario.vx <> 0. then "walk"
        else "stand"
    "images/mario" + verb + mario.dir + ".gif"
    |> Win.image
    |> Win.position (w/2.-16.+mario.x,  h-50.-31.-mario.y)
 | 
The last thing that needs to be done is to write the main function that drives
the game. The function does some initialization and then starts a recursive update
function that calculates a new game state using step and renders it in a loop.
The Keyboard helper module is discussed below.
|  1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
 | // Some initialization
Keyboard.init()
let w,h = Win.dimensions()
// Recursive function that updates the state & renders it
let rec update mario () =
    let mario = mario |> step (Keyboard.arrows())
    render (w,h) mario
    window.setTimeout(update mario, 1000. / 60.) |> ignore
// Start the game with Mario in the center
let mario = { x=0.; y=0.; vx=0.; vy=0.; dir="right" }
update mario ()
 | 
The Keyboard module handles keydown and keyup events of the window and
exposes them using the arrows property (which is a tuple int*int with -1 if the
left/up key is pressed, 1 if right/down key is pressed and 0 otherwise).
|  1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
 | module Keyboard =
  /// Set of currently pressed keys
  let mutable keysPressed = Set.empty
  /// Returns 1 if key with given code is pressed
  let code x =
    if keysPressed.Contains(x) then 1 else 0
  /// Update the state of the set for given key event
  let update (e : KeyboardEvent, pressed) =
      let keyCode = int e.keyCode
      let op =  if pressed then Set.add else Set.remove
      keysPressed <- op keyCode keysPressed
      null
  /// Returns pair with -1 for left or down and +1
  /// for right or up (0 if no or both keys are pressed)
  let arrows () =
    (code 39 - code 37, code 38 - code 40)
  let init () =
    document.addEventListener_keydown(fun e -> update(e, true))
    document.addEventListener_keyup(fun e -> update(e, false))
 | 
The Window module contains basic functionality for creating and rendering
window using HTML5 canvas and moving images around. The functionsbelow
fill the window, set position of image and create image.
|  1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
 | module Win =
  // Get the canvas context for drawing
  let canvas = document.getElementsByTagName_canvas().[0.]
  let context = canvas.getContext_2d()
  // Format RGB color as "rgb(r,g,b)"
  let ($) s n = s + n.ToString()
  let rgb r g b = "rgb(" $ r $ "," $ g $ "," $ b $ ")"
  /// Fill rectangle with given color
  let filled color rect =
      let ctx = context
      ctx.fillStyle <- U3.Case1 color
      ctx.fillRect rect
  /// Move element to a specified X Y position
  let position (x,y) (img : HTMLImageElement) =
      img.style.left <- x.ToString() + "px"
      img.style.top <- (canvas.offsetTop + y).ToString() + "px"
  let dimensions () =
    canvas.width, canvas.height
  /// Get the first <img /> element and set `src` (do
  /// nothing if it is the right one to keep animation)
  let image (src:string) =
      let image = document.getElementsByTagName_img().[0.]
      if image.src.IndexOf(src) = -1 then image.src <- src
      image
 | 
namespace Fable
namespace Fable.Core
namespace Fable.Import
module Browser
from Fable.Import
val max : a:'a -> b:'a -> 'a (requires comparison)
Full name: Mario.max
val a : 'a (requires comparison)
val b : 'a (requires comparison)
val mutable keysPressed : Set<int>
Full name: Mario.Keyboard.keysPressed
 Set of currently pressed keys
Multiple items
module Set
from Microsoft.FSharp.Collections
--------------------
type Set<'T (requires comparison)> =
  interface IComparable
  interface IEnumerable
  interface IEnumerable<'T>
  interface ICollection<'T>
  new : elements:seq<'T> -> Set<'T>
  member Add : value:'T -> Set<'T>
  member Contains : value:'T -> bool
  override Equals : obj -> bool
  member IsProperSubsetOf : otherSet:Set<'T> -> bool
  member IsProperSupersetOf : otherSet:Set<'T> -> bool
  ...
Full name: Microsoft.FSharp.Collections.Set<_>
--------------------
new : elements:seq<'T> -> Set<'T>
val empty<'T (requires comparison)> : Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Set.empty
val code : x:int -> int
Full name: Mario.Keyboard.code
 Returns 1 if key with given code is pressed
val x : int
member Set.Contains : value:'T -> bool
val update : e:KeyboardEvent * pressed:bool -> 'a (requires 'a : null)
Full name: Mario.Keyboard.update
 Update the state of the set for given key event
val e : KeyboardEvent
Multiple items
val KeyboardEvent : KeyboardEventType
Full name: Fable.Import.Browser.KeyboardEvent
--------------------
type KeyboardEvent =
  interface
    inherit UIEvent
    abstract member getModifierState : keyArg:string -> bool
    abstract member DOM_KEY_LOCATION_JOYSTICK : float
    abstract member DOM_KEY_LOCATION_LEFT : float
    abstract member DOM_KEY_LOCATION_MOBILE : float
    abstract member DOM_KEY_LOCATION_NUMPAD : float
    abstract member DOM_KEY_LOCATION_RIGHT : float
    abstract member DOM_KEY_LOCATION_STANDARD : float
    abstract member altKey : bool
    abstract member char : string
    ...
  end
Full name: Fable.Import.Browser.KeyboardEvent
val pressed : bool
val keyCode : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
property KeyboardEvent.keyCode: float
val op : (int -> Set<int> -> Set<int>)
val add : value:'T -> set:Set<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Set.add
val remove : value:'T -> set:Set<'T> -> Set<'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Set.remove
val arrows : unit -> int * int
Full name: Mario.Keyboard.arrows
 Returns pair with -1 for left or down and +1
 for right or up (0 if no or both keys are pressed)
val init : unit -> unit
Full name: Mario.Keyboard.init
val document : Document
Full name: Fable.Import.Browser.document
abstract member Document.addEventListener_keydown : listener:System.Func<KeyboardEvent,obj> * ?useCapture:bool -> unit
abstract member Document.addEventListener_keyup : listener:System.Func<KeyboardEvent,obj> * ?useCapture:bool -> unit
val canvas : HTMLCanvasElement
Full name: Mario.Win.canvas
abstract member Document.getElementsByTagName_canvas : unit -> NodeListOf<HTMLCanvasElement>
val context : CanvasRenderingContext2D
Full name: Mario.Win.context
abstract member HTMLCanvasElement.getContext_2d : unit -> CanvasRenderingContext2D
val s : string
val n : 'a
System.Object.ToString() : string
val rgb : r:'a -> g:'b -> b:'c -> string
Full name: Mario.Win.rgb
val r : 'a
val g : 'b
val b : 'c
val filled : color:string -> float * float * float * float -> unit
Full name: Mario.Win.filled
 Fill rectangle with given color
val color : string
val rect : float * float * float * float
val ctx : CanvasRenderingContext2D
property CanvasRenderingContext2D.fillStyle: U3<string,CanvasGradient,CanvasPattern>
type U3<'a,'b,'c> =
  | Case1 of 'a
  | Case2 of 'b
  | Case3 of 'c
Full name: Fable.Core.U3<_,_,_>
union case U3.Case1: 'a -> U3<'a,'b,'c>
abstract member CanvasRenderingContext2D.fillRect : x:float * y:float * w:float * h:float -> unit
val position : x:'a * y:float -> img:HTMLImageElement -> unit
Full name: Mario.Win.position
 Move element to a specified X Y position
val x : 'a
val y : float
val img : HTMLImageElement
Multiple items
val HTMLImageElement : HTMLImageElementType
Full name: Fable.Import.Browser.HTMLImageElement
--------------------
type HTMLImageElement =
  interface
    inherit HTMLElement
    abstract member align : string
    abstract member alt : string
    abstract member border : string
    abstract member complete : bool
    abstract member crossOrigin : string
    abstract member currentSrc : string
    abstract member height : float
    abstract member hspace : float
    abstract member isMap : bool
    ...
  end
Full name: Fable.Import.Browser.HTMLImageElement
property HTMLElement.style: CSSStyleDeclaration
property CSSStyleDeclaration.left: string
property CSSStyleDeclaration.top: string
property HTMLElement.offsetTop: float
val dimensions : unit -> float * float
Full name: Mario.Win.dimensions
property HTMLCanvasElement.width: float
property HTMLCanvasElement.height: float
val image : src:string -> HTMLImageElement
Full name: Mario.Win.image
 Get the first <img /> element and set `src` (do
 nothing if it is the right one to keep animation)
val src : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
val image : HTMLImageElement
abstract member Document.getElementsByTagName_img : unit -> NodeListOf<HTMLImageElement>
property HTMLImageElement.src: string
System.String.IndexOf(value: string) : int
System.String.IndexOf(value: char) : int
System.String.IndexOf(value: string, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int) : int
System.String.IndexOf(value: char, startIndex: int) : int
System.String.IndexOf(value: string, startIndex: int, comparisonType: System.StringComparison) : int
System.String.IndexOf(value: string, startIndex: int, count: int) : int
System.String.IndexOf(value: char, startIndex: int, count: int) : int
System.String.IndexOf(value: string, startIndex: int, count: int, comparisonType: System.StringComparison) : int
type Mario =
  {x: float;
   y: float;
   vx: float;
   vy: float;
   dir: string;}
Full name: Mario.Mario
Mario.x: float
Multiple items
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = System.Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
Mario.y: float
Mario.vx: float
Mario.vy: float
Mario.dir: string
val jump : 'a * y:int -> m:Mario -> Mario
Full name: Mario.jump
val y : int
val m : Mario
val gravity : m:Mario -> Mario
Full name: Mario.gravity
val physics : m:Mario -> Mario
Full name: Mario.physics
val walk : x:int * 'a -> m:Mario -> Mario
Full name: Mario.walk
val dir : string
val step : int * int -> mario:Mario -> Mario
Full name: Mario.step
val dir : int * int
val mario : Mario
val render : w:float * h:float -> mario:Mario -> unit
Full name: Mario.render
 Render mario on canvas
val w : float
val h : float
module Win
from Mario
val verb : string
module Keyboard
from Mario
val w : float
Full name: Mario.w
val h : float
Full name: Mario.h
val update : mario:Mario -> unit -> unit
Full name: Mario.update
val window : Window
Full name: Fable.Import.Browser.window
abstract member WindowTimers.setTimeout : handler:obj * ?timeout:obj * [<System.ParamArray>] args:obj [] -> float
val ignore : value:'T -> unit
Full name: Microsoft.FSharp.Core.Operators.ignore
val mario : Mario
Full name: Mario.mario