diff --git a/Dorkbox-Util/src/dorkbox/util/swing/ActiveRenderLoop.java b/Dorkbox-Util/src/dorkbox/util/swing/ActiveRenderLoop.java
new file mode 100644
index 0000000..dd384e5
--- /dev/null
+++ b/Dorkbox-Util/src/dorkbox/util/swing/ActiveRenderLoop.java
@@ -0,0 +1,88 @@
+package dorkbox.util.swing;
+
+import dorkbox.util.ActionHandlerLong;
+import dorkbox.util.SystemProps;
+
+import javax.swing.JFrame;
+import java.awt.Graphics;
+import java.awt.Toolkit;
+import java.awt.image.BufferStrategy;
+
+/**
+ * Loop that controls the active rendering process
+ */
+class ActiveRenderLoop implements Runnable {
+ @SuppressWarnings("WhileLoopReplaceableByForEach")
+ @Override
+ public
+ void run() {
+ long lastTime = System.nanoTime();
+
+ // 30 FPS is usually just fine. This isn't a game where we need 60+ FPS. We permit this to be changed though, just in case it is.
+ final int TARGET_FPS = SystemProps.ActiveRenderTargetFPS;
+ final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
+ Graphics graphics = null;
+
+ while (SwingActiveRender.hasActiveRenders) {
+ long now = System.nanoTime();
+ long updateDeltaNanos = now - lastTime;
+ lastTime = now;
+
+ // not synchronized, because we don't care. The worst case, is one frame of animation behind.
+ for (int i = 0; i < SwingActiveRender.activeRenderEvents.size(); i++) {
+ ActionHandlerLong actionHandlerLong = SwingActiveRender.activeRenderEvents.get(i);
+
+ //noinspection unchecked
+ actionHandlerLong.handle(updateDeltaNanos);
+ }
+
+ for (int i = 0; i < SwingActiveRender.activeRenders.size(); i++) {
+ JFrame jFrame = SwingActiveRender.activeRenders.get(i);
+
+ final BufferStrategy buffer = jFrame.getBufferStrategy();
+
+ // maybe the frame was closed
+ if (buffer != null) {
+ try {
+ graphics = buffer.getDrawGraphics();
+ jFrame.paint(graphics);
+ } catch (IllegalStateException ignored) {
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (graphics != null) {
+ graphics.dispose();
+
+ // blit the back buffer to the screen
+ if (!buffer.contentsLost()) {
+ buffer.show();
+ }
+ }
+ }
+ }
+ }
+
+ // Sync the display on some systems (on Linux, this fixes event queue problems)
+ Toolkit.getDefaultToolkit()
+ .sync();
+
+ try {
+ // Converted to int before the division, because IDIV is
+ // 1 order magnitude faster than LDIV (and int's work for us anyways)
+ // see: http://www.cs.nuim.ie/~jpower/Research/Papers/2008/lambert-qapl08.pdf
+ // Also, down-casting (long -> int) is not expensive w.r.t IDIV/LDIV
+ //noinspection NumericCastThatLosesPrecision
+ final int l = (int) (lastTime - System.nanoTime() + OPTIMAL_TIME);
+ final int millis = l / 1000000;
+ if (millis > 1) {
+ Thread.sleep(millis);
+ }
+ else {
+ // try to keep the CPU from getting slammed. We couldn't match our target FPS, so loop again
+ Thread.yield();
+ }
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+}
diff --git a/Dorkbox-Util/src/dorkbox/util/swing/NullRepaintManager.java b/Dorkbox-Util/src/dorkbox/util/swing/NullRepaintManager.java
new file mode 100644
index 0000000..d84964c
--- /dev/null
+++ b/Dorkbox-Util/src/dorkbox/util/swing/NullRepaintManager.java
@@ -0,0 +1,48 @@
+package dorkbox.util.swing;
+
+import javax.swing.JComponent;
+import javax.swing.RepaintManager;
+import javax.swing.SwingUtilities;
+
+/**
+ * The NullRepaintManager is a RepaintManager that doesn't do any repainting. Useful when all of the rendering is done manually by the
+ * application.
+ */
+public
+class NullRepaintManager extends RepaintManager {
+ /**
+ * Installs the NullRepaintManager onto the EDT (WARNING: This disables painting/rendering by the EDT, for the entire JVM)
+ */
+ public static
+ void install() {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public
+ void run() {
+ RepaintManager repaintManager = new NullRepaintManager();
+ repaintManager.setDoubleBufferingEnabled(false);
+ RepaintManager.setCurrentManager(repaintManager);
+ }
+ });
+ }
+
+ public
+ void addInvalidComponent(JComponent c) {
+ // do nothing
+ }
+
+ public
+ void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
+ // do nothing
+ }
+
+ public
+ void markCompletelyDirty(JComponent c) {
+ // do nothing
+ }
+
+ public
+ void paintDirtyRegions() {
+ // do nothing
+ }
+}
diff --git a/Dorkbox-Util/src/dorkbox/util/swing/SwingActiveRender.java b/Dorkbox-Util/src/dorkbox/util/swing/SwingActiveRender.java
new file mode 100644
index 0000000..50875ec
--- /dev/null
+++ b/Dorkbox-Util/src/dorkbox/util/swing/SwingActiveRender.java
@@ -0,0 +1,161 @@
+package dorkbox.util.swing;
+
+import dorkbox.util.ActionHandlerLong;
+
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import java.awt.Component;
+import java.awt.EventQueue;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Contains all of the appropriate logic to setup and render via "Active" rendering (instead of "Passive" rendering). This permits us to
+ * render JFrames (and their contents), OFF of the EDT - even though there are other frames/components that are ON the EDT.
Because we
+ * still want to react to mouse events, etc on the EDT, we do not completely remove the EDT -- we merely allow us to "synchronize" the EDT
+ * object to our thread. It's a little bit hacky, but it works beautifully, and permits MUCH nicer animations.
+ *