GameLisp is a scripting language for Rust game development. It was created while working on The Castle on Fire.

  • No garbage collection pauses. GameLisp has a unique garbage collector designed specifically for game development. It runs once per frame, every frame, without causing any latency spikes.

  • Seamless Rust API. Integrating GameLisp into a Rust codebase is effortless, thanks to Rust's powerful type system. Installation is trivial - it's just a crate!

  • Memory-safe. GameLisp is implemented entirely in Rust, with very few dependencies. By default, its implementation doesn't use unsafe at all.

  • Feature-rich. GameLisp has all of the convenience features you might expect from a modern language. Pattern‑matching, iterators, coroutines, macros...

  • Easy entity scripting. GameLisp has a unique object system, built around state machines and mixins, designed specifically for scripting game entities.

If you're interested, take a look at the Getting Started page for more information.

(defstruct Rect
  x y w h

  (meth overlaps? (other-rect)
    (let [x y w h] other-rect)
    (and (< @x (+ x w))
         (< x (+ @x @w))
         (< @y (+ y h))
         (< y (+ @y @h)))))

(def paddle-speed 220)
(def paddle-height 40)
(def paddle-start-y (-> play:height (- paddle-height) (/ 2)))

(def left-paddle (Rect
  (x 10)
  (y paddle-start-y)
  (w 6)
  (h paddle-height)))

(def right-paddle (Rect
  (x (- play:width 16)) 
  (y paddle-start-y)
  (w 6)
  (h paddle-height)))

(def ball-start-x (-> play:width (/ 2) (- 3)))
(def ball-start-y (-> play:height (/ 2) (- 3)))

(def ball (Rect
  (x ball-start-x)
  (y ball-start-y)
  (w 6)
  (h 6)))

(def ball-dx 0)
(def ball-dy 0)

(defn play:update (dt)

  ; update the paddles
  (for (paddle up-key down-key) in `((~left-paddle w s)
                                     (~right-paddle up down))
    (when (play:down? up-key)
      (dec! [paddle 'y] (* dt paddle-speed)))

    (when (play:down? down-key)
      (inc! [paddle 'y] (* dt paddle-speed)))

    (clamp! [paddle 'y] 0 (- play:height paddle-height)))

  ; update the ball
  (when (and (== ball-dx ball-dy 0)
             (any? play:pressed? '(w s up down)))
    (= ball-dx (* (rand-select -1 1) (rand 170 210)))
    (= ball-dy (* (rand-select -1 1) (rand 50 100))))

  (inc! [ball 'x] (* dt ball-dx))
  (inc! [ball 'y] (* dt ball-dy))

  (when (< [ball 'y] 0)
    (= ball-dy (abs ball-dy)))

  (when (>= (+ [ball 'y] [ball 'h]) play:height)
    (= ball-dy (- (abs ball-dy))))

  (when (or (and (.overlaps? ball left-paddle) (< ball-dx 0))
            (and (.overlaps? ball right-paddle) (> ball-dx 0)))
    (= ball-dx (- (* ball-dx (rand 1.03 1.08))))
    (inc! ball-dy (rand 50 -50))
    (clamp! ball-dy (- (abs ball-dx)) (abs ball-dx)))

  (unless (<= 0 [ball 'x] play:width)
    (= [ball 'x] ball-start-x)
    (= [ball 'y] ball-start-y)
    (= ball-dx 0)
    (= ball-dy 0))

  ; rendering
  (let midnight-blue '(25 25 112))
  (let turquoise '(64 224 208))

  (play:fill 0 0 play:width play:height ..midnight-blue)
  (play:fill ..[ball '(x y w h)] ..turquoise)
  (play:fill ..[left-paddle '(x y w h)] ..turquoise)
  (play:fill ..[right-paddle '(x y w h)] ..turquoise))

The above code implements a simple game. Give it a try on the playground!