Nehe Tutorials in Clojure using Penumbra
Pull request Compare This branch is even with swannodette:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Running the Tutorials

You need to have installed Leiningen 1.1.0. You can then clone this repo in the usual way:

git clone git://

Once you’ve done that run the following from your clone if you’re using Emacs/SLIME or Enclojure:

lein deps
lein native-deps
lein swank

You can then connect to this REPL. In Emacs/SLIME this can be done with M-x slime-connect and just press the enter key twice to use the defaults.

I highly recommend using Emacs/SLIME or Enclojure. It’s useful when you’re starting out to be able to jump to source definitions in Penumbra. In Emacs this can be done with M-. (that’s Meta-period).

If somebody has more elaborate instructions for Enclojure, VimClojure, etc I would love to add them to wiki. I plan on getting to this eventually but I’m an Emacs person.


If you pass a display proxy function to your app you can interact with the tutorials without having to restart the application completely. Now if you redefine display or any of the functions it calls your Penumbra app will update immediately. Interactive coding for the win!

(defn display [[delta time] state]
  (translate -1.5 0 -6)
   (doall (map #(apply vertex %) *tri*)))
  (translate 3 0 0)
   (doall (map #(apply vertex %) *sqr*)))

(defn display-proxy [& args]
  (apply display args))

(def app-options {:reshape reshape
                  :display display-proxy
                  :init init})

(defn start []
  (app/start app-options {}))

Converting OpenGL API names

Penumbra’s approach OpenGL programming is very different from the one most adopted by most OpenGL binding libraries. Most libraries simply expose the C API with little fanfare. However, Penumbra is an opinionated peice of software and tries to remove as much tedium from OpenGL programming as possible. This may be slightly disturbing at first because it’s not immediately obvious how to translate OpenGL code you might find on the web to work under Penumbra. The following highlights some guiding principles.

Naming Conventions

For the most part names have been shorted. You don’t have to write the gl prefix before everything:




Type Hints

Often you’ll find your self adding type hints in Clojure to avoid the overhead of reflection. However, you really don’t need to bother type hinting arguments to any of Penumbra’s fns. They will be converted to the proper representation.

That being said be careful with division. Without type coercision dividing two integers will result in a Ratio. You can avoid this like so:

(/ (double 5) 100) ; -> 0.05

glBegin() and glEnd()

Also you’ll rarely see glBegin() or glEnd() in Penumbra code. You see something like draw-[option]:

glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f);


  (vertex 1 1 1)
  (vertex -1 1 1)
  (vertex -1 -1 1)
  (vertex 1 -1 1))

Notice the lack of type hints or even bothering to write the numbers in their floating point represenation.

In OpenGL you often specify many state options via OpenGL constants. In Penumbra you can do this with keywords, again dropping the gl prefix. Some examples:



(enable :depth-test)
(depth-test :lequal)
(hint :perspective-correction-hint :nicest)
(shade-model :smooth)

Trouble Shooting

Missing functions

Some of the OpenGL API exposed by Lightweight Java Game Library (LWJGL) has not been brought into Penumbra. However it’s simple to import these things yourself with gl-import. For example glClearDepth has not been imported, but you just can import it yourself like so:

(use '[penumbra.opengl.core :only [gl-import]])
(gl-import glClearDepth clear-depth)


The signatures (or arglists) of your methods are really important. For example if you provide a reshape fn it must look something like the following:

(defn reshape [[x y width height] state]

You can get a full list of required signatures here,

It also important that if you use app/start with options you must also pass in a map for the state.

(app/start callback-map {})

If you don’t your program will crash immediately.