Fork me on GitHub

Hey there, How can I start writing a membrane backend? I tried copying skia.clj into my clojure project, and forking it in a git submodule. Either way, it can't get a working classpath. Missing and com.phronemophobic.membrane.Skia. Tried running clj -M:deploy in the submodule, thinking it might build Skia, but it's missing a pom.xml.


writing a membrane backend isn't really documented, but I can try to help throw together some resources


what kind of backend are you trying to make?


as a template, I would start with the membrane.java2d namespace since it doesn't have any external dependencies


Thanks, good idea. I just got a local java2d fork working. Hoping to experiment with a game loop.


Not sure if I need a game loop. I like the idea of writing an app that could work on any backend. But I'm not sure how to get an animation to run while no input events are happening. Maybe just changing to (glfw-call void glfwWaitEventsTimeout (double 1/60)) would be enough.


Oh! I just found the new skia repaint function. Maybe that's what I needed.


I wouldn't mix and match functions between membrane.skia and membrane.java2d. When you run a ui with membrane.java2d/run, it returns a map of window info that includes a repaint function and the jframe window. (see In general, I probably wouldn't recommend calling any of the internal membrane.skia functions unless you're familiar with skia and native programming. The native calls in membrane.skia are a little trickier to use since incorrect usage can cause memory corruption or hard crash the jvm.


Another interesting option might be to create a based backend.


Or depending on what you're looking for, you might not need membrane at all, but the benefit of creating a quil based backend is that you get a bunch of membrane tools and components "for free".


Yeah, I was thinking about quil. I would like to implement more components for drawing. I'm also wondering if membrane could abstract over something like Making progress on the game loop. It's amazing how concise membrane can be:

(ns kimok.membrane-game
  (:require [clojure.core.async :refer [<! timeout go-loop]]
            [tick.core :as time]
	        [membrane.ui   :as ui]
            [membrane.skia :as skia]))

(def state (atom {:frame-number 0
                  :last-time (time/now)}))

(def window (skia/run #(ui/label @state)))

(defn on-frame []
  (let [now (time/now)
        {:keys [frame-number last-time]} @state
        dt (time/micros (time/between last-time now))]
    (reset! state {:frame-number (inc frame-number)
                   :fps (int (/ 1000000 dt))
                   :last-time now
                   :dt (float (/ dt 1000000))})))

(go-loop []
  (<! (timeout 1000/60))
  ((::skia/repaint window))
Not sure if the metrics are accurate to what's really happening. It would be nice if the backend could pass them somehow. I tried the same with java2d on, but it still seems to wait on input events.


@U02J388JDEG , I came across a stackoverflow post recently that said you might need to call to get the screen to repaint on linux


oh wait, you're using the skia backend?


I'm sure you could extend support for membrane to play-clj.


Yeah, it does crash the repl here and there but this sketch seems stable.


when you say crash, do you mean "throw an exception and close the window" or jvm dies?


the latter


I've heard there's some issues on linux, but I've been unable to reproduce them


would you be able to share what you're using for clojure -Stree?


org.clojure/clojure 1.10.3 . org.clojure/spec.alpha 0.2.194 . org.clojure/core.specs.alpha 0.2.56


I'm more interested in the which membrane version and which skialib you're using


oh yeah, haha


org.clojure/clojure 1.11.1
  . org.clojure/spec.alpha 0.3.218
  . org.clojure/core.specs.alpha 0.2.62
net.sekao/odoyle-rules 0.11.0
  . expound/expound 0.7.2
  . cnuernber/dtype-next 8.041
    . org.ow2.asm/asm 9.0
    . insn/insn 0.5.2
      . org.ow2.asm/asm 9.0
    . camel-snake-kebab/camel-snake-kebab 0.4.2
    . it.unimi.dsi/fastutil 8.2.1
    . org.xerial.larray/larray-mmap 0.4.1
      . org.xerial.larray/larray-buffer 0.4.1
    . org.apache.commons/commons-math3 3.6.1
    . org.roaringbitmap/RoaringBitmap 0.9.0
      . org.roaringbitmap/shims 0.9.0
    . com.github.wendykierp/JTransforms 3.1
      X org.apache.commons/commons-math3 3.5 :older-version
      . 1.5
        X org.apache.commons/commons-math3 3.5 :older-version
    . techascent/tech.resource 5.04
      . org.clojure/tools.logging 1.1.0
    . 30.1.1-jre
      . 1.0.1
      . 9999.0-empty-to-avoid-conflict-with-guava
      . 3.0.2
      . org.checkerframework/checker-qual 3.8.0
      . 2.5.1
      . 1.3
  . org.apache.commons/commons-text 1.9
    . org.apache.commons/commons-lang3 3.11
  . net.n01se/clojure-jna 1.0.0
    X 4.0.0 :older-version
  . 5.10.0
  . com.rpl/specter 1.1.3
    . riddley/riddley 0.1.12
  . org.clojure/core.async 1.4.627
    . org.clojure/tools.analyzer.jvm 1.2.0
      . org.clojure/tools.analyzer 1.1.0
      . org.clojure/core.memoize 1.0.236
        . org.clojure/core.cache 1.0.207
          . org.clojure/data.priority-map 1.0.0
      X org.ow2.asm/asm 5.2 :older-version
      . org.clojure/tools.reader 1.3.2
tick/tick 0.5.0-RC6
  . com.widdindustries/ 0.1.21
    . com.widdindustries/ 0.1.20
  . cljsjs/js-joda-timezone 2.2.0-0
  . cljsjs/js-joda-locale-en-us 3.1.1-1
  . com.widdindustries/time-literals 0.1.10


when it crashed, did you switch between java2d and skia without restarting the repl?


the two backends are not friendly with eachother


Actually, swiching between them or running both at once seems to work pretty well here.


I think that might work for a bit, but my guess is that might the cause leading to eventual crashes


Not sure if I can pinpoint the cause, but I'll let you know if I can reproduce something.


this works for me on swing. I'm wondering if adding a call to .sync would help the java2d issues on linux Something like:

(ns kimo
  (:require [clojure.core.async :refer [<! timeout go-loop]
             :as async]
            [tick.core :as time]
	    [membrane.ui   :as ui]
            [membrane.java2d :as backend])
  (:import java.awt.Toolkit))

(def default-toolkit (Toolkit/getDefaultToolkit))

(def state (atom {:frame-number 0
                  :last-time (time/now)}))

(defn on-frame [now]
  (let [{:keys [frame-number last-time]} @state
        dt (time/micros (time/between last-time now))]
    (reset! state {:frame-number (inc frame-number)
                   :fps (int (/ 1000000 dt))
                   :last-time now
                   :dt (float (/ dt 1000000))})))

(defonce running? (atom false))


  (def window (backend/run #(ui/label @state)))

    (reset! running? true)
    (loop []
      (let [t (time/now)]

        (on-frame t)
        (async/<!! (timeout (- 1000/80
                               (/ (time/micros
                                   (time/between t (time/now)))
        ((::backend/repaint window))
        (.sync ^Toolkit default-toolkit))

      (when @running?


Thanks, I'll study this. Works so far.

🎉 1

can you confirm that the call to .sync is the fix?


if that's the case, I'll probably make an update to the java2d backend to call .sync on repaint


Yeah, it is.

gratitude 1

By the way, still interested in figuring out how to build membrane/skia. I'd like to contribute to issues like


if you're looking to build the native dependencies, you can check out the github workflow,


I think the issue with vsync is essentially a glfw issue (on mac osx)


on linux, it might be as simple as: • setting the glfwswapinterval to 1, • use glfwPollEvents to wait for events instead of glfwWaitEventsTimeout , see • for normal UI that doesn't normally change every frame, this will unnecessarily consume a bunch of cpu, so it's probably a good idea to only redraw if the view has changed since the last frame.


Nice, I'll check it out


I'd be open to a pull request that makes this configurable as long as the defaults "just work" on linux and mac

👍 1