Basic 3D rendering in SVG: elm-playground-3d

Lucamug
Lucamug
Nov 17 · 5 min read

Code: https://github.com/lucamug/elm-playground-3d
Demos: https://elm-playground-3d.netlify.com/
Example: https://elmjapan.org/

“floating city”

elm-playground-3d is an experimental library built on top of elm-playground. So let me introduce shortly this library first…

elm-playground

elm-playground is an excellent library to “create pictures, animations, and games with Elm”. It is at the same time simple to use so beginner can learn the basic of programming and also powerful to build complex applications.

It wraps The Elm Architecture providing simple directives to draw objects on the screen (square, circle, triangle, etc.) and to transform them (move, rotate, scale).

It has

  • Three levels of difficulty that you can follow during your learning process: picture, animation, game
  • Information about the screen, the mouse and the keyboard for creating an interactive application
  • Hooks to move object, such as spin, wave, zigzag

The library support two dimensional shapes but I needed to build a simple three dimensional composition and I want to maintain all the features that elm-playground provides, keeping the same level of simplicity

Let’s go 3D!

So I built a simple 3D layer above elm-playground. This is a comparison of usage between the 2D and the 3D versions:

2D

main =
picture
[ square purple 500
|> fade 0.8
|> move 200 0
]

3D

main =
picture
[ cube darkPurple purple lightPurple 500
|> fade3d 0.8
|> move3d 200 0 0
|> shape3dto2d camera1
]

As you can see the two processes follow the same logic. The subtle differences are:

  • Functions in 2D have, when possible, an analogues in 3D with a “3d” suffix in the name. The suffix allows to import everything from the 3D library without generating conflicts with functions from the 2D library.
  • Functions in 3D may need extra arguments, such as move3d that require the extra Z coordinate.
  • 3D shapes require to be converted to 2D using shape3dto2d before processing them with 2D functions (such as picture).

The magic is made by the shape3dto2d function. What it does is called 3D projection. I used perspective projection using ianmackenzie/elm-3d-camera. For the transformations in the 3D space I used ianmackenzie/elm-geometry.

These are advanced library for 3D transformations and you should definitely have a look at them if you want to do something serious with 3D rendering. For this library I simplified them, replacing several arguments with default values.

Following see three examples ordered by complexity (picture, animation, game)

Example 1 — Picture

Demo: https://elm-playground-3d.netlify.com/example1-picture.html

This example put together, side by side, 2D and 3D elements.

main =
picture
[ group3d
[ cube darkPurple purple lightPurple 600
|> fade3d 0.5
, words3d darkPurple "3D"
|> scale3d 5
]
|> shape3dto2d camera1
|> moveLeft 180
, group
[ square darkPurple 300
|> fade 0.5
, words darkPurple "2D"
|> scale 5
]
|> moveRight 180
]

Example 2 — Animation

Demo: https://elm-playground-3d.netlify.com/example2-animation.html

Here we add some action

main =
animation view

Example 3 — Game

Demo: https://elm-playground-3d.netlify.com/example3-game.html

This other example add interactions:

  • Horizontal movements of the mouse rotate all the objects on the screen. This is obtained adding the mouse coordinate to the rotate3d transformation:
rotate3d 0 0 (computer.mouse.x / 3)
  • The small cube is controlled horizontally with the arrows keys and vertically with the mouse, this is obtained with
move3d (500 - x * 30) (500 - y * 30) (500 + computer.mouse.y * 2)

Where x and y are coming from

update : Computer -> Memory -> Memory
update computer ( x, y ) =
let
( dx, dy ) =
toXY computer.keyboard
in
( x + dx, y + dy )

as explained in the elm-playground documentation

  • Extra modifications based on some pressed button. To detect if a button is pressed:
if Set.member "s" computer.keyboard.keys then
...

Pushing the envelope

With these simple blocks is possible to build fairly complicated composition, like the “floating city” in https://elmjapan.org/:

In this case we don’t only animate the object but we continuously animate the camera, giving the floating effect.

I will eventually release the source code of this website, stay tuned!

Conclusions

This library is still at an early stage and is missing many feature that a “serious” 3D library should support.

As a noobie in the 3D rendering field, this has been a lot of fun and also it has been simpler that I expected.

For more serious 3D stuff in Elm, have a look at

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade