bohonghuang/claw-raylib

Fully auto-generated Common Lisp bindings to Raylib (6.0/5.5) and Raygui (4.0) using claw and cffi-object

82

stars

35

commits

Common Lisp

primary language

Apr 23, 2026

updated

README

#+TITLE: claw-raylib
[[https://github.com/raysan5/raylib/raw/master/logo/raylib_logo_animation.gif]]

Fully auto-generate Common Lisp bindings to [[https://www.raylib.com/][Raylib]] (as well as Raymath, Rlgl and Raygui) using [[https://github.com/borodust/claw][claw]] and [[https://github.com/bohonghuang/cffi-object][cffi-object]]. 
* Installation
~claw-raylib~ and some of its dependencies are not available in Quicklisp currently.
You need to clone this repository and the following projects into the =local-projects= folder of your Quicklisp installation path:
- [[https://github.com/bohonghuang/cffi-object][cffi-object]]
- [[https://github.com/bohonghuang/cffi-ops][cffi-ops]]
* Build
~claw-raylib~ does not ship any prebuilt binary files.
You need to ensure that Raylib, Raygui, and a compatible C compiler are installed on your system to manually build the adapters required by ~claw~.

Tip: You can clone the [[https://github.com/bohonghuang/claw-raylib/tree/prebuild][prebuild]] branch to skip steps 1 and 2.

1. Clone, build and install [[https://github.com/borodust/libresect][libresect]], [[https://github.com/raysan5/raylib][raylib]] and [[https://github.com/raysan5/raygui][raygui]] (optional).
2. Generate the bindings using Clozure CL. \\
   #+BEGIN_SRC lisp
     (ql:quickload :claw-raylib/gen)
     (pushnew :claw-regen-adapter *features*)
     (cffi:load-foreign-library #P"/path/to/libresect.so")
     (claw:load-wrapper :raylib)
     (claw:load-wrapper :raygui)
     (claw:load-wrapper :rlgl)
   #+END_SRC

   /Notes for Windows:/
   
   Replace the line like: 

   #+BEGIN_SRC c
     #include <windows.h>
   #+END_SRC
   
   with:
   
   #+BEGIN_SRC c
     #include <minwindef.h>
     #define GetModuleHandle GetModuleHandleA
     HMODULE GetModuleHandleA(LPCTSTR name);
     FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
   #+END_SRC
3. Compile the adapters. \\
   #+BEGIN_SRC lisp
     (let ((arch "x86_64-pc-linux-gnu")
           (path (merge-pathnames #P"lib/" (asdf:component-pathname (asdf:find-system '#:claw-raylib)))))
       (dolist (lib '("raylib" "rlgl" "raygui"))
         (uiop:run-program
          (list "gcc" "-O3" "-fPIC" "-shared" "-o"
                (namestring (merge-pathnames (format nil "lib~A-adapter.so" lib) path))
                (namestring (merge-pathnames (format nil "lib~A-adapter.~A.c" lib arch) path))))))
   #+END_SRC
4. Load the system. \\
   #+BEGIN_SRC lisp
     (ql:quickload :claw-raylib)
   #+END_SRC
   
   /Notes for SBCL:/
   
   During this process, SBCL may consume a significant amount of memory, potentially leading to heap exhaustion.
   You may need to add ~--dynamic-space-size 4096~ to the SBCL command-line arguments before the first load of ~claw-raylib~.
* Features
** Up-to-date & Complete
Raylib, Rlgl, Raymath, and Raygui's low-level and high-level APIs are automatically generated using ~claw~ and ~cffi-object~,
along with some tricks during the build process. You can use any released version or even the Git version of Raylib without changing the ~claw-raylib~ version,
as ~claw-raylib~ can automatically generate the both levels of APIs. This also means that all APIs of these libraries are available, just like in C.

 #+BEGIN_SRC lisp
   (let ((camera (raylib:make-camera-3d :position (raylib:make-vector3 :x 10.0 :y 10.0 :z 10.0)
                                        :target (raylib:vector3-zero)
                                        :up (raylib:make-vector3 :x 0.0 :y 1.0 :z 0.0)
                                        :fovy 33.3 :projection #.(cffi:foreign-enum-value 'raylib:camera-projection :perspective)))
         (model (raylib:load-model "/path/to/model"))
         (position (raylib:vector3-zero)))
     (raylib:with-window ("Simple Model Viewer" (1280 720))
       (loop :until (raylib:window-should-close)
             :do (raylib:with-drawing
                   (raylib:update-camera camera #.(cffi:foreign-enum-value 'raylib:camera-mode :free))
                   (raylib:with-mode-3d camera
                     (raylib:clear-background raylib:+raywhite+)
                     (raylib:draw-grid 100 1.0)
                     (rlgl:disable-backface-culling)
                     (raylib:draw-model model position 1.0 raylib:+white+)
                     (rlgl:enable-backface-culling))))))
 #+END_SRC

A complete [[file:examples/raygui/controls-test-suite.lisp][example]] of using ~claw-raylib~ to rewrite the [[https://github.com/raysan5/raygui/blob/master/examples/controls_test_suite/controls_test_suite.c][control test suite]] from Raygui is available.
** High-performance
Thanks to the adapters generated by ~claw~, ~claw-raylib~ does not use ~cffi-libffi~ and incurs no expensive performance overhead (according to [[https://www.reddit.com/r/lisp/comments/ygebes/passing_c_struct_by_value_cffilibffi_is_250x/][this post]])
 or heavy GC pressure when calling functions that accept C structures as parameters (which is the case for most functions in Raylib).

 #+BEGIN_SRC lisp
   ;;; The overhead of FFI calls is no longer a performance bottleneck for the system.

   ;;            Self        Total        Cumul
   ;;   Nr  Count     %  Count     %  Count     %    Calls  Function
   ;; ------------------------------------------------------------------------
   ;;    1    261  32.2    261  32.2    261  32.2        -  foreign function rlVertex3f
   ;;    2    109  13.4    450  55.5    370  45.6        -  foreign function DrawTexturePro
   ;;    3     43   5.3     56   6.9    413  50.9        -  (LAMBDA (&OPTIONAL POSITION ORIGIN SCALE ROTATION TINT) :IN TILED-LAYER-RENDERER)
   ;;    4     31   3.8    277  34.2    444  54.7        -  foreign function rlVertex2f
   ;;    5     23   2.8     23   2.8    467  57.6        -  foreign function rlTexCoord2f
   ;;    6     18   2.2     18   2.2    485  59.8        -  foreign function __sched_yield
   ;;    7     16   2.0     19   2.3    501  61.8        -  foreign function rlSetTexture
   ;;    8     15   1.8    495  61.0    516  63.6        -  foreign function __claw_DrawTexturePro
   ;;    9     14   1.7     14   1.7    530  65.4        -  (LAMBDA (POSITION SCALE) :IN TILED-LAYER-RENDERER)
   ;;   10     11   1.4     11   1.4    541  66.7        -  foreign function rlBegin
 #+END_SRC
** High-level
~claw-raylib~ utilizes ~cffi-object~ to automatically wrap Raylib's types, allowing you to completely disregard memory concerns.
All types from Raylib can be seamlessly integrated into CLOS, and the API style remains highly similar to Common Lisp,
and for all structure parameters in FFI functions, ~cffi-object~ objects are passed by default instead of raw pointers,
greatly reducing the disconnect often associated with cross-language interoperations.

#+BEGIN_SRC lisp
  (raylib:vector2-normalize
   (raylib:vector2-add
    (raylib:make-vector2 :x 1.0 :y 2.0)
    (raylib:vector2-one)))
  ;; => #<VECTOR2 :X 0.5547002 :Y 0.8320503 @0x00007FF59C000D70>

  (raylib:fade (raylib:color-brightness (raylib:get-color #xCE42EFFF) -0.5) 0.5)
  ;; => #<COLOR :R 103 :G 33 :B 119 :A 127 @0x00007FF59C000E50>

  (defgeneric vector-add (v1 v2))

  (defmethod vector-add ((v1 raylib:vector2) (v2 raylib:vector2))
    (raylib:vector2-add v1 v2))

  (defmethod vector-add ((v1 raylib:vector3) (v2 raylib:vector3))
    (raylib:vector3-add v1 v2))

  (defmethod vector-add ((v1 raylib:vector4) (v2 raylib:vector4))
    (raylib:quaternion-add v1 v2))

  (vector-add (raylib:vector3-one) (raylib:vector3-one))
  ;; => #<VECTOR3 :X 2.0 :Y 2.0 :Z 2.0 @0x00007FF59C000ED0>
#+END_SRC
** Low-level
In performance-intensive scenarios, directly using the low-level functions exposed by ~claw-raylib~ (whose names are prefixed with ~%~)
in conjunction with [[https://github.com/bohonghuang/cffi-ops][cffi-ops]] for GC-free programming is a better choice. Modules written using this approach can achieve performance levels close to that of C.

#+BEGIN_SRC lisp
  (use-package :cffi-ops)

  (defun camera-3d-normalize (camera)
    (declare (optimize (speed 3)
                       (debug 0)
                       (safety 0)))
    (clet* ((camera (cthe (:pointer (:struct raylib:camera-3d)) (& camera)))
            (up (& (-> camera raylib:up)))
            (right up)
            (look (foreign-alloca '(:struct raylib:vector3)))) ; Stack memory allocation
      (raylib:%vector3-subtract look (& (-> camera raylib:target)) (& (-> camera raylib:position)))
      (raylib:%vector3-cross-product right look up)
      (raylib:%vector3-cross-product up right look)
      (raylib:%vector3-normalize up up))
    camera)
#+END_SRC
* Examples
[[file:examples/screenshots/controls-test-suite.png]]

See the [[file:examples/][examples]] directory. To run all examples, eval this in your REPL:

#+BEGIN_SRC lisp
  (ql:quickload :claw-raylib/examples)
  (do-external-symbols (symbol :claw-raylib.examples)
    (funcall symbol))
#+END_SRC

Opening a PR for contributions is welcome. Encountering any problem, feel free to open an issue.
* Related Projects
- [[https://github.com/longlene/cl-raylib][cl-raylib]]: A manually written Raylib binding that uses 3d-matrics and 3d-vectors as the math library.
  It offers high usability but is not suitable for performance-intensive scenarios due to its use of cffi-libffi.
  For example, it may not be suitable for a Tiled map renderer that requires pretty frequent calls to ~DrawTexturePro~.
- [[https://github.com/defun-games/claylib][claylib]]: A game framework based on Raylib that provides practical features for game development,
  such as scenes and interactive programming. It also utilizes ~claw~ to automatically generate low-level bindings for Raylib
  and then manually writes the exported high-level APIs. ~claw-raylib~, on the other hand, is just a Lispy Raylib wrapper that does not offer any functionality beyond Raylib itself.
  It is suitable for cases where you only want to use Raylib  or develop your own game engine/framework based on Raylib in Common Lisp.
* Projects using ~claw-raylib~
- [[https://github.com/bohonghuang/eon][eon]]: An easy-to-use but flexible game framework for Common Lisp.
- [[https://github.com/bohonghuang/cl-universal-tween-engine][cl-universal-tween-engine]]: Common Lisp port of the Universal Tween Engine, whose [[https://github.com/bohonghuang/cl-universal-tween-engine/tree/master/demo][demo]] is made by ~claw-raylib~.

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

bohonghuang

33 commits

BanchouBoo

1 commits

ToreAad

1 commits

bohonghuang/claw-raylib

Fully auto-generated Common Lisp bindings to Raylib (6.0/5.5) and Raygui (4.0) using claw and cffi-object

82

stars

35

commits

Common Lisp

primary language

Apr 23, 2026

updated

README

#+TITLE: claw-raylib
[[https://github.com/raysan5/raylib/raw/master/logo/raylib_logo_animation.gif]]

Fully auto-generate Common Lisp bindings to [[https://www.raylib.com/][Raylib]] (as well as Raymath, Rlgl and Raygui) using [[https://github.com/borodust/claw][claw]] and [[https://github.com/bohonghuang/cffi-object][cffi-object]]. 
* Installation
~claw-raylib~ and some of its dependencies are not available in Quicklisp currently.
You need to clone this repository and the following projects into the =local-projects= folder of your Quicklisp installation path:
- [[https://github.com/bohonghuang/cffi-object][cffi-object]]
- [[https://github.com/bohonghuang/cffi-ops][cffi-ops]]
* Build
~claw-raylib~ does not ship any prebuilt binary files.
You need to ensure that Raylib, Raygui, and a compatible C compiler are installed on your system to manually build the adapters required by ~claw~.

Tip: You can clone the [[https://github.com/bohonghuang/claw-raylib/tree/prebuild][prebuild]] branch to skip steps 1 and 2.

1. Clone, build and install [[https://github.com/borodust/libresect][libresect]], [[https://github.com/raysan5/raylib][raylib]] and [[https://github.com/raysan5/raygui][raygui]] (optional).
2. Generate the bindings using Clozure CL. \\
   #+BEGIN_SRC lisp
     (ql:quickload :claw-raylib/gen)
     (pushnew :claw-regen-adapter *features*)
     (cffi:load-foreign-library #P"/path/to/libresect.so")
     (claw:load-wrapper :raylib)
     (claw:load-wrapper :raygui)
     (claw:load-wrapper :rlgl)
   #+END_SRC

   /Notes for Windows:/
   
   Replace the line like: 

   #+BEGIN_SRC c
     #include <windows.h>
   #+END_SRC
   
   with:
   
   #+BEGIN_SRC c
     #include <minwindef.h>
     #define GetModuleHandle GetModuleHandleA
     HMODULE GetModuleHandleA(LPCTSTR name);
     FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
   #+END_SRC
3. Compile the adapters. \\
   #+BEGIN_SRC lisp
     (let ((arch "x86_64-pc-linux-gnu")
           (path (merge-pathnames #P"lib/" (asdf:component-pathname (asdf:find-system '#:claw-raylib)))))
       (dolist (lib '("raylib" "rlgl" "raygui"))
         (uiop:run-program
          (list "gcc" "-O3" "-fPIC" "-shared" "-o"
                (namestring (merge-pathnames (format nil "lib~A-adapter.so" lib) path))
                (namestring (merge-pathnames (format nil "lib~A-adapter.~A.c" lib arch) path))))))
   #+END_SRC
4. Load the system. \\
   #+BEGIN_SRC lisp
     (ql:quickload :claw-raylib)
   #+END_SRC
   
   /Notes for SBCL:/
   
   During this process, SBCL may consume a significant amount of memory, potentially leading to heap exhaustion.
   You may need to add ~--dynamic-space-size 4096~ to the SBCL command-line arguments before the first load of ~claw-raylib~.
* Features
** Up-to-date & Complete
Raylib, Rlgl, Raymath, and Raygui's low-level and high-level APIs are automatically generated using ~claw~ and ~cffi-object~,
along with some tricks during the build process. You can use any released version or even the Git version of Raylib without changing the ~claw-raylib~ version,
as ~claw-raylib~ can automatically generate the both levels of APIs. This also means that all APIs of these libraries are available, just like in C.

 #+BEGIN_SRC lisp
   (let ((camera (raylib:make-camera-3d :position (raylib:make-vector3 :x 10.0 :y 10.0 :z 10.0)
                                        :target (raylib:vector3-zero)
                                        :up (raylib:make-vector3 :x 0.0 :y 1.0 :z 0.0)
                                        :fovy 33.3 :projection #.(cffi:foreign-enum-value 'raylib:camera-projection :perspective)))
         (model (raylib:load-model "/path/to/model"))
         (position (raylib:vector3-zero)))
     (raylib:with-window ("Simple Model Viewer" (1280 720))
       (loop :until (raylib:window-should-close)
             :do (raylib:with-drawing
                   (raylib:update-camera camera #.(cffi:foreign-enum-value 'raylib:camera-mode :free))
                   (raylib:with-mode-3d camera
                     (raylib:clear-background raylib:+raywhite+)
                     (raylib:draw-grid 100 1.0)
                     (rlgl:disable-backface-culling)
                     (raylib:draw-model model position 1.0 raylib:+white+)
                     (rlgl:enable-backface-culling))))))
 #+END_SRC

A complete [[file:examples/raygui/controls-test-suite.lisp][example]] of using ~claw-raylib~ to rewrite the [[https://github.com/raysan5/raygui/blob/master/examples/controls_test_suite/controls_test_suite.c][control test suite]] from Raygui is available.
** High-performance
Thanks to the adapters generated by ~claw~, ~claw-raylib~ does not use ~cffi-libffi~ and incurs no expensive performance overhead (according to [[https://www.reddit.com/r/lisp/comments/ygebes/passing_c_struct_by_value_cffilibffi_is_250x/][this post]])
 or heavy GC pressure when calling functions that accept C structures as parameters (which is the case for most functions in Raylib).

 #+BEGIN_SRC lisp
   ;;; The overhead of FFI calls is no longer a performance bottleneck for the system.

   ;;            Self        Total        Cumul
   ;;   Nr  Count     %  Count     %  Count     %    Calls  Function
   ;; ------------------------------------------------------------------------
   ;;    1    261  32.2    261  32.2    261  32.2        -  foreign function rlVertex3f
   ;;    2    109  13.4    450  55.5    370  45.6        -  foreign function DrawTexturePro
   ;;    3     43   5.3     56   6.9    413  50.9        -  (LAMBDA (&OPTIONAL POSITION ORIGIN SCALE ROTATION TINT) :IN TILED-LAYER-RENDERER)
   ;;    4     31   3.8    277  34.2    444  54.7        -  foreign function rlVertex2f
   ;;    5     23   2.8     23   2.8    467  57.6        -  foreign function rlTexCoord2f
   ;;    6     18   2.2     18   2.2    485  59.8        -  foreign function __sched_yield
   ;;    7     16   2.0     19   2.3    501  61.8        -  foreign function rlSetTexture
   ;;    8     15   1.8    495  61.0    516  63.6        -  foreign function __claw_DrawTexturePro
   ;;    9     14   1.7     14   1.7    530  65.4        -  (LAMBDA (POSITION SCALE) :IN TILED-LAYER-RENDERER)
   ;;   10     11   1.4     11   1.4    541  66.7        -  foreign function rlBegin
 #+END_SRC
** High-level
~claw-raylib~ utilizes ~cffi-object~ to automatically wrap Raylib's types, allowing you to completely disregard memory concerns.
All types from Raylib can be seamlessly integrated into CLOS, and the API style remains highly similar to Common Lisp,
and for all structure parameters in FFI functions, ~cffi-object~ objects are passed by default instead of raw pointers,
greatly reducing the disconnect often associated with cross-language interoperations.

#+BEGIN_SRC lisp
  (raylib:vector2-normalize
   (raylib:vector2-add
    (raylib:make-vector2 :x 1.0 :y 2.0)
    (raylib:vector2-one)))
  ;; => #<VECTOR2 :X 0.5547002 :Y 0.8320503 @0x00007FF59C000D70>

  (raylib:fade (raylib:color-brightness (raylib:get-color #xCE42EFFF) -0.5) 0.5)
  ;; => #<COLOR :R 103 :G 33 :B 119 :A 127 @0x00007FF59C000E50>

  (defgeneric vector-add (v1 v2))

  (defmethod vector-add ((v1 raylib:vector2) (v2 raylib:vector2))
    (raylib:vector2-add v1 v2))

  (defmethod vector-add ((v1 raylib:vector3) (v2 raylib:vector3))
    (raylib:vector3-add v1 v2))

  (defmethod vector-add ((v1 raylib:vector4) (v2 raylib:vector4))
    (raylib:quaternion-add v1 v2))

  (vector-add (raylib:vector3-one) (raylib:vector3-one))
  ;; => #<VECTOR3 :X 2.0 :Y 2.0 :Z 2.0 @0x00007FF59C000ED0>
#+END_SRC
** Low-level
In performance-intensive scenarios, directly using the low-level functions exposed by ~claw-raylib~ (whose names are prefixed with ~%~)
in conjunction with [[https://github.com/bohonghuang/cffi-ops][cffi-ops]] for GC-free programming is a better choice. Modules written using this approach can achieve performance levels close to that of C.

#+BEGIN_SRC lisp
  (use-package :cffi-ops)

  (defun camera-3d-normalize (camera)
    (declare (optimize (speed 3)
                       (debug 0)
                       (safety 0)))
    (clet* ((camera (cthe (:pointer (:struct raylib:camera-3d)) (& camera)))
            (up (& (-> camera raylib:up)))
            (right up)
            (look (foreign-alloca '(:struct raylib:vector3)))) ; Stack memory allocation
      (raylib:%vector3-subtract look (& (-> camera raylib:target)) (& (-> camera raylib:position)))
      (raylib:%vector3-cross-product right look up)
      (raylib:%vector3-cross-product up right look)
      (raylib:%vector3-normalize up up))
    camera)
#+END_SRC
* Examples
[[file:examples/screenshots/controls-test-suite.png]]

See the [[file:examples/][examples]] directory. To run all examples, eval this in your REPL:

#+BEGIN_SRC lisp
  (ql:quickload :claw-raylib/examples)
  (do-external-symbols (symbol :claw-raylib.examples)
    (funcall symbol))
#+END_SRC

Opening a PR for contributions is welcome. Encountering any problem, feel free to open an issue.
* Related Projects
- [[https://github.com/longlene/cl-raylib][cl-raylib]]: A manually written Raylib binding that uses 3d-matrics and 3d-vectors as the math library.
  It offers high usability but is not suitable for performance-intensive scenarios due to its use of cffi-libffi.
  For example, it may not be suitable for a Tiled map renderer that requires pretty frequent calls to ~DrawTexturePro~.
- [[https://github.com/defun-games/claylib][claylib]]: A game framework based on Raylib that provides practical features for game development,
  such as scenes and interactive programming. It also utilizes ~claw~ to automatically generate low-level bindings for Raylib
  and then manually writes the exported high-level APIs. ~claw-raylib~, on the other hand, is just a Lispy Raylib wrapper that does not offer any functionality beyond Raylib itself.
  It is suitable for cases where you only want to use Raylib  or develop your own game engine/framework based on Raylib in Common Lisp.
* Projects using ~claw-raylib~
- [[https://github.com/bohonghuang/eon][eon]]: An easy-to-use but flexible game framework for Common Lisp.
- [[https://github.com/bohonghuang/cl-universal-tween-engine][cl-universal-tween-engine]]: Common Lisp port of the Universal Tween Engine, whose [[https://github.com/bohonghuang/cl-universal-tween-engine/tree/master/demo][demo]] is made by ~claw-raylib~.

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

bohonghuang

33 commits

BanchouBoo

1 commits

ToreAad

1 commits

Languages

Common Lisp

100.0%