Slack3D Maven central

Simple 3D graphics engine.



  • Phong lighting
  • Fly style camera - Freely move around a 3D scene
  • Text rendering - English characters and numbers.
  • Out of the box Shapes - Vector, Box, Circle, Cone, Cylinder, HeightField, Plane, Point, Pyramid, Sphere, Tetrahedron, Triangle, Line & Ray
  • OpenGL via LWJGL3
  • User input to transform and rotate shapes.
  • Basic linear algebra library - Matrix3, Matrix2, Vector3 & Vector2
  • Immutable API

Key controls

  • W, A, S, D - Move along all 3 axes
  • Mouse drag - Changes camera view position
  • Enter - Return to initial camera view
  • Space (Hold!) - Pauses rendering/animation (camera is still active)


libraryDependencies += "com.github.simerplaha" %% "slack3d" % "0.1.0"


Allow LWJGL to run on main thread with -XstartOnFirstThread in VM options. If you are using IntelliJ set the flag in the "Build and run" window.



Slack3D can be thought of a collection instance where your code provide shapes to render for each frame.

Render a sphere of radius 0.5 of colour Purple.

Slack3D("A sphere") foreach {
  state => //State of current render
    //shapes to render
    Seq(Sphere(radius = 0.5, colour = Colour.Purple))


Interval rendering

Render shapes after some time interval. The following will render a Sphere of radius 0.5 with a new colour after every 1.second.

Slack3D("Colour").foreach(interval = 1.second) {
  _ =>
    Seq(Sphere(0.5, colour =


All OpenGL colours can be found class type Colour. To get the next colour from a randomly sorted colour queue


Apply Y axis rotation to the box.

Slack3D("Rotating Box") foreach {
  state =>
    //same code as above but with added rotation at Y axis
    val box = Box(Colour.Red).rotateY(state.getTime() * 30)


Rotation can be applied to all axis. See APIs


Rotation with user inputs

Configures the Box to be

  • translatable when Z key is pressed and arrows keys are moved
  • rotatable when X key is pressed and arrows keys are moved
Slack3D("Custom rotation").foldLeft(Box()) {
  case (_box, state) =>
    val box =
        .translatable(state.window, GLFW.GLFW_KEY_Z) //translate box when Z key is pressed
        .rotatable(state.window, GLFW.GLFW_KEY_X) //rotate box when X key is pressed

    (box, Seq(box))



Create 2 vectors where the third vector is a cross product.

Slack3D("Lines and vectors") foreach {
  state =>
    val vector1 = Vector3(0.5, -0.5, 0)
    val vector2 = Vector3(0.5, 0.5, 0)
    //cross product
    val cross = vector1 cross vector2

      Line(vector1, Colour.Red),
      Line(vector2, Colour.Yellow),
      Line(cross, Colour.Green)


All Lines and Points will render a text displaying the position and length of that vector.


You can change the width, height, title, camera etc. See the following example.

  title = "My configurations", //window title
  width = 800, //window width
  height = 600, //window height
  backgroundColor = Colour.Orange,
  enableWireframes = true, //configures OpenGL in wireframes mode. 
  camera = None, // disable camera or provide your custom instance. See how default works
  enable2DCoordinates = false, //enables or disables coordinate drawing
  perspective = None, //perspective view
  light = None //configures lighting 
) foreach {
  state =>
    //no shapes to render

Custom shapes

All shapes are a combination of primitives Triangle, Line or a Point.

 * A custom shape that render text and a line
case class MyCustomShape(text: Text,
                         line: Cylinder) extends Shape {

  override type Self = MyCustomShape

  //function to apply to each vector on this shape.
  //this function is used for applying perspective, rotation & translation
  override def map(f: Vector3[Double] => Vector3[Double]): MyCustomShape =
      text =,
      line =

  //expands each shape into points, lines and triangles
  override def buildMesh(mesh: Mesh[Point, LineOrRay, Triangle]): Unit = {
    text buildMesh mesh
    line buildMesh mesh

//Start Slack3D instance and render the above custom shape
Slack3D("My custom shape") foreach {
  state =>
    //create my custom shape instance
    val myShape =
        text = Text("My custom shape text", Colour.White) * 2 + Vector3(-0.6, 0.4), //a scaled & translated custom text
        line = Cylinder(Colour.Purple) / 2 //A scaled down cylinder




Thank you Jetbrains for providing an open-source licence for their awesome development tools.

Jetbrains support