This post is part of the F# Advent Calendar in English 2017. Thank you Sergey, who’s been keeping the F# community updated weekly for over 5 years!

We’re going to take a look at how to get started creating cool stuff with Fable and PixiJS. PixiJS is a a JavaScript library that creates very fast 2D WebGL content. It’s great for creating graphical visualizations and games. Combining Fable with the PixiJS bindings allows us to do this with F#.

Gearing up for getting down

If you’re playing along at home, you’ll need these:

Installing the template

To make Fable PixiJS apps easy to set up, the fable-pixi-template was created. Assuming you have a working .NET Core environment, you can install the template from the command line like so:

dotnet new -i Fable.Template.Pixi

Creating a new app

Once the template is installed, you can use it to create a simple app to base your project on:

dotnet new fable-pixi -n PixiTest

This will create a PixiTest directory and generate some scaffolding for the template app. The app is a Fable port of the PixiJS Basics example. To run it, you first need to install some JavaScript dependencies:

cd PixiTest
yarn install

…then install some .NET dependencies:

cd src
dotnet restore

…then compile and run:

dotnet fable yarn-start

This will invoke the Fable compiler which will compile the F# source into JavaScript, then use webpack to bundle the project and serve it using the webpack dev server on port 8080. Assuming everything went well, you should be able to open a browser to http://localhost:8080 and see a rotating Fable logo:

Fable + PixiJS is awesome

App Source

The F# source for the app can be found in the src/App.fs file. It’s pretty short (I’ve ommitted comments):

module PixiTest

open Fable.Core.JsInterop
open Fable.Import.Pixi
open Fable.Import.Browser

let options = jsOptions<PIXI.ApplicationOptions> (fun o ->
    o.backgroundColor <- Some 0x000000
)

let app = PIXI.Application(400., 400., options)
document.body.appendChild(app.view) |> ignore

let bunny = PIXI.Sprite.fromImage("fable_logo_small.png")

let renderer : PIXI.WebGLRenderer = !!app.renderer

bunny.anchor.set(0.5)
bunny.x <- renderer.width * 0.5
bunny.y <- renderer.height * 0.5

app.stage.addChild(bunny) |> ignore

let tick delta =
  bunny.rotation <- bunny.rotation + 0.1 * delta

app.ticker.add(tick) |> ignore

Source Tour

Let’s break the code down, starting from the beginning:

module PixiTest

open Fable.Core.JsInterop
open Fable.Import.Pixi
open Fable.Import.Browser

After the module statement which defines our only module, some F# libraries are imported:

  • Fable.Core.JsInterop: Converts F# data to/from JavaScript objects, JSON, provides operators for dealing with JavaScript, etc.
  • Fable.Import.Pixi: F# PixiJS bindings to the PixiJS project.
  • Fable.Import.Browser: F# bindings for the DOM/BOM.
let options = jsOptions<PIXI.ApplicationOptions> (fun o ->
    o.backgroundColor <- Some 0x000000
)

When creating a PixiJS application, an options object allows you to set various Application settings. We use it here to set the background color to 0x000000 (black). Fable provides jsOptions as a way to create type-safe F# objects that will compile to POJOs. The JS result of the options object is { "backgroundColor": 0 }.

The backgroundColor property is an option type (int option), so a Some expression is used to provide a value.

Note that the options object could also have been created using the createEmpty function:

let options = createEmpty<PIXI.ApplicationOptions>

options.backgroundColor <- Some 0x000000

See the Plain Old JavaScript Objects section of the Fable documentation for more info.

let app = PIXI.Application(400., 400., options)
document.body.appendChild(app.view) |> ignore

Here we create a PixiJS Application and append its view to the document’s body. The height and width of our app as well as the options object are used to create the Application. The result of the appendChild call is a Node. If we don’t use the Node result, the F# compiler will warn us, so we pipe it to ignore.

let bunny = PIXI.Sprite.fromImage("fable_logo_small.png")

The term “bunny” comes from the PixiJS project’s PixiJS Basics example that this Fable version was based on. Here we’re using the snazzy Fable dragon logo image to create a PIXI.Sprite. A Sprite is just an image that can be rendered to the screen.

The fable_logo_small.png logo image is served from the public directory of the project (in this case by webpack’s dev server if you invoked the fable yarn-start command earlier).

let renderer : PIXI.WebGLRenderer = !!app.renderer

I’m not 100% sure on this one, but I believe this is creating a renderer of type PIXI.WebGLRenderer and dynamically casting (the double-bang function) the application’s renderer to it.

bunny.anchor.set(0.5)
bunny.x <- renderer.width * 0.5
bunny.y <- renderer.height * 0.5

app.stage.addChild(bunny) |> ignore

Each Sprite object has an anchor point that defines an origin location for the Sprite. When positioning a Sprite by setting its x and y coordinates, the anchor point of the Sprite is what gets moved to the x and y coordinates. The initial anchor point of a PixiJS Sprite is 0, 0 (the top-left of the Sprite). Here we’re changing the anchor point to the be center of the image. We then set the Sprite’s x and y coordinates to the center of the renderer (200, since 400 was used for the width and height earlier).

We then add the Sprite object to the Application’s stage. The stage is the app’s root container. A container holds a collection of display objects. Our stage only contains one display object: the logo Sprite.

let tick delta =
  bunny.rotation <- bunny.rotation + 0.1 * delta

app.ticker.add(tick) |> ignore

PixiJS provides a Ticker class that runs an update loop. The Ticker class has an add function that allows you to add a listener function. When the Ticker’s loop iterates, it will call the listener function, passing it the time since the last animation frame, called delta. Using the delta value allows us to update the rotation in a time-based manner rather than tying the animation to whatever framerate it happens to run in.

And that’s the Basic template app. If you have the app running using the webpack dev server, you can make changes to the source and Fable will compile it and your browser will refresh with the changes.

Hopefully this post provided you with enough to get started making your own Fable and PixiJS creations.

Where to Go Next