Declarative graphics for the browser using GHCJS

Latest on Hackage:

This package is not currently in any snapshots. If you're interested in using it, we recommend adding it to Stackage Nightly. Doing so will make builds more reliable, and allow to host generated Haddocks.

MIT licensed by Francesco Gazzetta
Maintained by Francesco Gazzetta

Shine - Declarative Graphics for the Web

Build Status

Shine wraps javascript’s drawing functions in a declarative API.

Heavily inspired by gloss.

demo (compiled tests) here


You need ghcjs

A great way of building ghcjs packages is to use the reflex platform



To represent your drawing you have to build a tree using the Picture datatype.

pic :: Picture
pic = Rect 10 20 -- represents a 10x20 square

To compose multiple Pictures you can use Over, which accepts two Pictures and overlaps them.

Picture is a monoid: <> is an alias for Over and mempty is the empty picture.

-- draw some shapes on top of each other
pic :: Picture
pic = Rect 10 20
   <> Translate 30 30 (Circle 15)
   <> Colored (Color 255 0 0 0.2) (RectF 4 4)
   <> Text "Sans 12px" LeftAlign 200 "The quick brown fox jumps over the lazy dog."

Using Foldable you can do things like

concentricCircles :: Picture
concentricCircles = foldMap Circle [1,10..100]

Drawing Pictures

Before drawing anything you need to obtain a CanvasRenderingContext2D (and sometimes a Document). For this purpose, shine provides two utility functions: fullScreenCanvas and fixedSizeCanvas


import Graphics.Shine
import Graphics.Shine.Input
import Graphics.Shine.Picture

import GHCJS.DOM (currentDocumentUnchecked)

-- This is how the ghcjs-dom hello-world does it.
-- It's boilerplate, so in the next shine version there
-- will probably be a ready-to-use run function
#if defined(ghcjs_HOST_OS)
run :: a -> a
run = id
#elif defined(MIN_VERSION_jsaddle_wkwebview)
import Language.Javascript.JSaddle.WKWebView (run)
import Language.Javascript.JSaddle.WebKitGTK (run)

main :: IO ()
main = run $ do
    doc <- currentDocumentUnchecked -- use currentDocument to handle failure
    ctx <- fixedSizeCanvas doc 400 400
    -- do something with ctx (and maybe doc)

To render a Picture on a context you have three options:


You can draw it manually using render from Graphics.Shine.Render

main :: IO ()
    {- get the context, see before -}
    draw ctx concentricCircles


You can draw a Picture that depends on time. That is, a Float -> Picture.

-- An expanding-and-contracting circle.
animation :: Float -> Picture
animation = Translate 200 200
          . Circle
          . (*100) . (+1) -- bigger positive oscillation
          . sin -- the circle's radius oscillates

main :: IO ()
main =  do
    {- get the context, see before -}
    animate ctx 30 animation


Finally, you can draw a Picture that depends on time, inputs (keyboard and mouse) and an internal state. This is especially useful for games, hence the name.

-- this code draws a black rectangle in the center of the canvas only when the
-- left mouse button is pressed
main :: IO ()
main = do
    {- get the context and the document, see before -}
    play ctx doc 30 initialState draw handleInput step
    -- our state represents the state of the left mouse button
    initialState = Up
    -- we draw a square only if the button is pressed
    draw Up = Empty
    draw Down = Translate 200 200 $ RectF 200 200
    -- when an event is fired we store the button state
    handleInput (MouseBtn BtnLeft buttonState _) = const buttonState
    handleInput _ = id -- catch-all for all other events
    step _ = id -- our state does not depend on time


Revision history for shine – 2016-04-20

  • First version. – 2017-06-23

  • Changed WIndow to Document in some places
  • Use Double for everything
  • Use the new ghcjs-dom types
Depends on 7 packages:
Used by 1 package:
comments powered byDisqus