Library to use HTML5 Canvas from Go-WASM, with all drawing within go code

go-canvas

go-canvas is a pure go+webassembly Library for efficiently drawing on a html5 canvas element within the browser from go without requiring calls back to JS to utilise canvas drawing functions.

The library provides the following features:

  • Abstracts away the initial DOM interactions to setup the canvas.
  • Creates the shadow image frame, and graphical Context to draw on it.
  • Initializes basic font cache for text using truetype font.
  • Sets up and handles requestAnimationFrame callback from the browser.

Concept

go-canvas takes an alternate approach to the current common methods for using canvas, allowing all drawing primitives to be done totally with go code, without calling JS.

standard syscall way

In a standard WASM application for canvas, the go code must create a function that responds to requestAnimationFrame callbacks and renders the frame within that call. It interacts with the canvas drawing primitives via the syscall/js functions and context switches. i.e.

laserCtx.Call("beginPath")
laserCtx.Call("arc", gs.laserX, gs.laserY, gs.laserSize, 0, math.Pi*2, false)
laserCtx.Call("fill")
laserCtx.Call("closePath")

Downsides of this approach (for me at least), are messy JS calls which can't easily be checked at compile time, forcing a full redraw every frame, even if nothing changed on that canvas, or changes being much slower than the requested frame rate.

go native way

go-canvas allows all drawing to be done natively using Go by creating an entirely separate image buffer which is drawn to using a 2D drawing library. I'm currently using one from https://github.com/llgcode/draw2d which provides most of the standard canvas primitives and more. This shadow Image buffer can be updated at whatever rate the developer deems appropriate, which may very well be slower than the browsers animation rate.

This shadow Image buffer is then copied over to the browser canvas buffer during each requestAnimationFrame callback, at whatever rate the browser requests. The handling of the callback and copy is done automatically within the library.

Secondly, this also allows the option of drawing to the image buffer, outside of the requestAnimationFrame callback if required. After some testing it appears that it is still best to do the drawing within the requestAnimationFrame callback.

go-canvas provides several options to control all this, and take care of the browser/dom interactions

  • User specifies the go render/draw callback method when calling the START function. This callback passes the graphical context to the render routine.
  • Render routine can choose to return whether any drawing took place. If it returns false, then the requestAnimationFrame callback does nothing, just returns immediately, saving CPU cycles. (No point to copy buffers and redraw if nothing has changed) This allows the drawing to be adaptive to the rate of data changes.
  • The 'start' function accepts a maxFPS parameter. The library will automatically throttle the requestAnimationFrame callback to only do redraws or image buffer copies to this max rate. Note it MAY be slower depending on the Render time, and the requirements of the browser doing other work. When a tab is hidden, the browser regularly reduces and may even stop call to the animation callback. No critical timing should be done in the render/draw routings.
  • You may pass 'nil' for the render function. In this case all drawing happens totally under the users control, outside of the library. This may be more useful in future when WASM supports proper threading. Right now however, testing shows it is slower as all work is in the one thread, and you lose the scheduling benefits of the requestAnimationFrame call.

Drawing therefore, is pure go. i.e.

func Render(gc *draw2dimg.GraphicContext) bool {
    // {some movement code removed for clarity, see the demo code for full function}
    // draws red 🔴 laser
    gc.SetFillColor(color.RGBA{0xff, 0x00, 0x00, 0xff})
    gc.SetStrokeColor(color.RGBA{0xff, 0x00, 0x00, 0xff})

    gc.BeginPath()
    gc.ArcTo(gs.laserX, gs.laserY, gs.laserSize, gs.laserSize, 0, math.Pi*2)
    gc.FillStroke()
    gc.Close()
return true  // Yes, we drew something, copy it over to the browser

If you do want to render outside the animation loop, a simple way to cause the code to draw the frame on schedule, independent from the browsers callbacks, is to use time.Tick. An example is in the demo app below.

If however your image is only updated from user input or some network activity, then it would be straightforward to fire the redraw only when required from these inputs. This can be controlled within the Render function, by just returning FALSE at the start. Nothing is draw, nor copied (saving CPU time) and the previous frames data remains.

Known issues !

There is currently a likely race condition for long draw functions, where the requestAnimationFrame may get a partially completed image buffer. This is more likely the longer the user render operation takes. Currently think how best to handle this, ideally without locks. Turns out this is not an issue, due to the single threaded nature. Eventually if drawing is in a separate thread, this will have to be handled.

Demo

A simple demo can be found in: ./demo directory. This is a shameless rewrite of the 'Moving red Laser' demo by Martin Olsansky https://medium.freecodecamp.org/webassembly-with-golang-is-fun-b243c0e34f02

Compile with GOOS=js GOARCH=wasm go build -o main.wasm

Includes a Caddy configuration file to support WASM, so will serve by just running 'caddy' in the demo directory and opening browser to http://localhost:8080

Live

Live Demo available at: https://markfarnan.github.io/go-canvas

Future

This library was written after a weekend of investigation and posted on request for the folks on #webassembly on Gophers Slack.

I intend to extend it further, time permitting, into fully fledged support package for all things go-canvas-wasm related, using this image frame method.

Several of the ideas I'm considering are:

  • Support for layered canvas, at least 3 for 'background', 'action' and 'user interaction'
  • Traps & helper functions for mouse interactions over the canvas
  • Unit tests - soon as I figure out how to do tests for WASM work.
  • Performance improvements in the image buffer copy - https://github.com/agnivade/shimmer/blob/c073303a81ab9a90b6fc14eb6d90c3a1b930025e/load_image_cb.go#L40 has been suggested as a place to start.
  • Detect if nothing has changed for the frame, and if so, don't even recopy the buffer, saving yet more time. May be useful for layers that change less frequently.
  • Multiple draw / render frames to fix the 'incomplete image' problem. -- Not actually a problem
  • Tidy up the close/end frame functionality to properly release resources on page unload and prevent 'browser reload errors' due to missing animation callback function.
  • Update for Go 1.13 and Go Modules
  • Add FPS Calculator metric

Others ? Feedback, suggestions etc. welcome. I can be found on Gophers Slack, #Webassembly channel.

Mark Farnan, February 2020

Comments
  • Changed imgCopy function to work in go 1.13

    Changed imgCopy function to work in go 1.13

    Function TypedArrayOf() was removed in Go 1.13. This commit replaces it with CopyBytesToJS.

    This is a fix for #1, needs to be tested more (I only did very little testing)

  • Memory / RAM Problem in Live Demo

    Memory / RAM Problem in Live Demo

    Hey there, looks like a really cool project. However, when I open the live demo I see massive memory usage. The application consumes around 10 GB in 30 seconds or so, before either the browser or I kill it. Problem tested and observed on the following:

    • Windows + Chrome 78
    • Windows + Firefox 74
    • Arch Linux + Firefox 76

    Don't know if this is a sign of problems with the library or if it is specific to the demo though. Let me know if you'd like any more info from me.

  • Error with Go 1.13 (TypedArrayOf has been removed)

    Error with Go 1.13 (TypedArrayOf has been removed)

    I am unable to use go-canvas with Go version 1.13.

    According to the go release notes for version 1,13

    TypedArrayOf has been replaced by CopyBytesToGo and CopyBytesToJS for copying bytes between a byte slice and a Uint8Array.

    I think because the TypedArrayOf function is used here the program does not run on my machine.

  • Remove copybuff

    Remove copybuff

    https://github.com/markfarnan/go-canvas/blob/6971ccd00770ac5ee39846076d21935a933364ee/canvas/canvas2d.go#L178

    It is possible to copy c.image.Pix ( []uint8 ) to c.imgData.Get("data") ( Uint8ClampedArray ) in one shot:

    js.CopyBytesToJs(c.imgData.Get("data"), c.image.Pix)
    

    So c.copybuff is not needed anymore.

  • Make this work with TinyGo

    Make this work with TinyGo

    In progress. Currently Fails to run ( TinyGo 0.13), and Fails to compile (0.14-DEV).

    This is due to missing support in TinyGo for sync/Atomic package, parts of which are used by std Lib "image" library, which is in turn used by image/png

    See: https://github.com/tinygo-org/tinygo/issues/1262

    A second issue is the embedded Font causes tinyGo to take ages to compile (20 mins) rather than 10s.
    Will look to refactor this to separate into an example, as really fonts should be an add-in, not part of the base library.

DOM library for Go and WASM

Go DOM binding (and more) for WebAssembly This library provides a Go API for different Web APIs for WebAssembly target. It's in an active development,

Dec 23, 2022
Run WASM tests inside your browser

wasmbrowsertest Run Go wasm tests easily in your browser. If you have a codebase targeting the wasm platform, chances are you would want to test your

Dec 16, 2022
An Experimental Wasm Virtual Machine for Gophers

gasm A minimal implementation of v1 WASM spec compatible virtual machine purely written in go. The vm can be embedded in your go program without any d

Dec 31, 2022
Golang-WASM provides a simple idiomatic, and comprehensive API and bindings for working with WebAssembly for Go and JavaScript developers
Golang-WASM provides a simple idiomatic, and comprehensive API and bindings for working with WebAssembly for Go and JavaScript developers

A bridge and bindings for JS DOM API with Go WebAssembly. Written by Team Ortix - Hamza Ali and Chan Wen Xu. GOOS=js GOARCH=wasm go get -u github.com/

Dec 22, 2022
Go Wasm is a in-browser IDE for Go

Go Wasm Go Wasm is a Go development environment with the essentials to write and run code entirely within the browser, using the power of WebAssembly

Jan 8, 2023
A WASM Filter for Envoy Proxy written in Golang

envoy-proxy-wasm-filter-golang A WASM Filter for Envoy Proxy written in Golang Build tinygo build -o optimized.wasm -scheduler=none -target=wasi ./mai

Nov 6, 2022
Istio wasm api demo with golang

istio-wasm-api-demo 1. Setup the latest Istio Setup k8s cluster: e.g. kind create cluster --name test Download the latest Istioctl from the GitHub rel

Nov 1, 2022
Material Design Components for use with Vecty in the most minimalistic fashion.

mdc Material Design Components for use with Vecty in the most minimalistic, dead simple fashion. Currently on MDC version 13.0.0. Based on the good wo

Mar 6, 2022
Fast face detection, pupil/eyes localization and facial landmark points detection library in pure Go.
Fast face detection, pupil/eyes localization and facial landmark points detection library in pure Go.

Pigo is a pure Go face detection, pupil/eyes localization and facial landmark points detection library based on Pixel Intensity Comparison-based Objec

Dec 24, 2022
Vugu: A modern UI library for Go+WebAssembly (experimental)

Vugu: A modern UI library for Go+WebAssembly (experimental)

Jan 3, 2023
Interact with browser from Go. Manually-crafted WebAPI interoperation library.

GWeb: golang + js + wasm gweb -- strictly typed WebAPI library on top of syscall/js. Like flow or TypeScript but for Go. You need it if you want to in

Sep 26, 2022
The note-tinygo Go library for communicating with Blues Wireless Notecard via serial or I²C

Blues Wireless The note-tinygo Go library for communicating with Blues Wireless Notecard via serial or I²C. This library allows you to control a Notec

Nov 29, 2021
This library provides WebAssembly capability for goja Javascript engine

This module provides WebAssembly functions into goja javascript engine.

Jan 10, 2022
Canvas is a Go drawing library based on OpenGL or using software rendering that is very similar to the HTML5 canvas API
Canvas is a Go drawing library based on OpenGL or using software rendering that is very similar to the HTML5 canvas API

Go canvas Canvas is a pure Go library that provides drawing functionality as similar as possible to the HTML5 canvas API. It has nothing to do with HT

Jan 3, 2023
Aes for go and java; build go fo wasm and use wasm parse java response.

aes_go_wasm_java aes for go and java; build go fo wasm and use wasm parse java response. vscode setting config settings.json { "go.toolsEnvVars":

Dec 14, 2021
3D Wireframe Drawing Library for Go
3D Wireframe Drawing Library for Go

pinhole 3D Wireframe Drawing Library for Go Javascript Version Demo Why does this exist? I needed a CPU based 3D rendering library with a very simple

Dec 10, 2022
Get all the swap details within the block range

go-swap-statistics get all the swap details within the block range get started git clone https://github.com/huahuayu/go-swap-statistics.git cd go-swap

Jul 22, 2022
This project parses all mails from google-search within key-words and ban-words

mailParser This project parses all mails from google-search within key-words and ban-words For launch program create the input file first string conta

Feb 2, 2022
Fully featured, spec-compliant HTML5 server-sent events library

go-sse Lightweight, fully spec-compliant HTML5 server-sent events library. Table of contents go-sse Table of contents Installation and usage Implement

Dec 5, 2022
Cairo in Go: vector to SVG, PDF, EPS, raster, HTML Canvas, etc.
Cairo in Go: vector to SVG, PDF, EPS, raster, HTML Canvas, etc.

Canvas is a common vector drawing target that can output SVG, PDF, EPS, raster images (PNG, JPG, GIF, ...), HTML Canvas through WASM, and OpenGL. It h

Dec 25, 2022