diff --git a/Dashboard.Drawing.OpenGL/DrawCallRecorder.cs b/Dashboard.Drawing.OpenGL/DrawCallRecorder.cs
index 5b6a482..58e6a77 100644
--- a/Dashboard.Drawing.OpenGL/DrawCallRecorder.cs
+++ b/Dashboard.Drawing.OpenGL/DrawCallRecorder.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.Contracts;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using OpenTK.Graphics.OpenGL;
@@ -224,4 +225,243 @@ namespace Dashboard.Drawing.OpenGL
             public int CmdIndex;
         }
     }
+
+    /// 
+    /// A customizable immediate mode draw call queue, for the modern OpenGL user.
+    /// 
+    /// The call info type.
+    /// The vertex structure.
+    public abstract class DrawCallRecorder : IGLDisposable, IInitializer
+        where TVertex : unmanaged
+    {
+        /// 
+        /// The vertex array for this queue.
+        /// 
+        public int Vao { get; private set; }
+
+        /// 
+        /// The vertex buffer for this queue.
+        /// 
+        public int Vbo { get; private set; }
+
+        /// 
+        /// Number of calls recorded in this queue.
+        /// 
+        public int CallCount => Calls.Count;
+
+        /// 
+        /// The number of total vertices recorded.
+        /// 
+        public int TotalVertices => Vertices.Count;
+
+        /// 
+        /// The latest draw call info.
+        /// 
+        public ref TCall CurrentCall => ref _currentCall;
+
+        /// 
+        /// The latest vertex emitted.
+        /// 
+        public ref TVertex CurrentVertex => ref _currentVertex;
+
+        /// 
+        /// True if currently recording a draw call.
+        /// 
+        public bool InCall => _primitiveMode != 0;
+
+        /// 
+        /// Size of one vertex.
+        /// 
+        protected int VertexSize => Unsafe.SizeOf();
+
+        /// 
+        /// The list of draw calls.
+        /// 
+        protected List Calls { get; } = new List();
+
+        /// 
+        /// The list of all vertices.
+        /// 
+        protected List Vertices { get; } = new List();
+
+        /// 
+        /// The value to write for the draw call info at the start of a call.
+        /// 
+        [Pure] protected virtual TCall DefaultCall => default(TCall);
+
+        /// 
+        /// The value to write for last vertex at the start of a call.
+        /// 
+        [Pure] protected virtual TVertex DefaultVertex => default;
+
+        private int _start = 0;
+        private int _count = 0;
+        private int _primitiveMode = 0;
+        private TCall _currentCall;
+        private TVertex _currentVertex;
+
+        protected DrawCallRecorder()
+        {
+            _currentCall = DefaultCall;
+            _currentVertex = DefaultVertex;
+        }
+
+        /// 
+        /// Record a draw call directly.
+        /// 
+        /// The primitive type to use.
+        /// The call info structure to use
+        /// The list of vertices to use.
+        /// You attempted to use this function during another draw call.
+        public void DrawArrays(PrimitiveType type, in TCall callInfo, ReadOnlySpan vertices)
+        {
+            if (InCall)
+                throw new InvalidOperationException("Cannot use draw arrays in the middle of an ongoing immediate-mode call.");
+
+            DrawCall call = new DrawCall()
+            {
+                Type = type,
+                Start = Vertices.Count,
+                Count = vertices.Length,
+                CallInfo = callInfo,
+            };
+
+            Vertices.AddRange(vertices);
+            Calls.Add(call);
+        }
+
+        /// 
+        /// Start a draw call.
+        /// 
+        /// The primitive type for the call.
+        public void Begin(PrimitiveType type) => Begin(type, DefaultCall);
+
+        /// 
+        /// Start a draw call.
+        /// 
+        /// The primitive type for the call.
+        /// The call info.
+        /// You attempted to create a draw call within a draw call.
+        public void Begin(PrimitiveType type, TCall callInfo)
+        {
+            if (InCall)
+                throw new InvalidOperationException("Attempt to begin new draw call before finishing previous one.");
+
+            _primitiveMode = (int)type;
+            _start = Vertices.Count;
+            _count = 0;
+            CurrentCall = callInfo;
+            CurrentVertex = DefaultVertex;
+        }
+
+        /// 
+        /// Emit the latest or modified vertex.
+        /// 
+        public void Vertex()
+        {
+            Vertex(CurrentVertex);
+        }
+
+        /// 
+        /// Emit a vertex.
+        /// 
+        /// The vertex to emit.
+        public void Vertex(in TVertex vertex)
+        {
+            Vertices.Add(CurrentVertex = vertex);
+            _count++;
+        }
+
+        /// 
+        /// End the current call.
+        /// 
+        /// You tried to end a call that you didn't begin recording.
+        public void End()
+        {
+            if (!InCall)
+                throw new InvalidOperationException("Attempt to end draw call before starting one.");
+
+            Calls.Add(new DrawCall()
+            {
+                Start = _start,
+                Count = _count,
+                Type = (PrimitiveType)_primitiveMode,
+                CallInfo = CurrentCall,
+            });
+
+            _primitiveMode = 0;
+        }
+
+        /// 
+        /// Called by the execution engine before a draw call is executed.
+        /// 
+        /// The call to prepare.
+        protected abstract void PrepareCall(in TCall call);
+
+        /// 
+        /// Set the vertex format for the  and  created by the recorder.
+        /// 
+        protected abstract void SetVertexFormat();
+
+        /// 
+        /// Execute all the recorded draw calls.
+        /// 
+        public void Execute()
+        {
+            GL.BindVertexArray(Vao);
+            GL.BindBuffer(BufferTarget.ArrayBuffer, Vbo);
+
+            ReadOnlySpan vertices = CollectionsMarshal.AsSpan(Vertices);
+            GL.BufferData(BufferTarget.ArrayBuffer, Vertices.Count * VertexSize, vertices, BufferUsage.DynamicDraw);
+
+            foreach (DrawCall call in Calls)
+            {
+                PrepareCall(call.CallInfo);
+                GL.DrawArrays(call.Type, call.Start, call.Count);
+            }
+        }
+
+        /// 
+        /// Clear the draw call queue.
+        /// 
+        public void Clear()
+        {
+            Vertices.Clear();
+            Calls.Clear();
+        }
+
+        public void Dispose()
+        {
+            throw new NotImplementedException();
+        }
+
+        public void Dispose(bool safeExit)
+        {
+            throw new NotImplementedException();
+        }
+
+        public bool IsInitialized { get; private set; }
+        public void Initialize()
+        {
+            if (IsInitialized)
+                return;
+            IsInitialized = true;
+
+            Vao = GL.CreateVertexArray();
+            Vbo = GL.CreateBuffer();
+
+            GL.BindVertexArray(Vao);
+            GL.BindBuffer(BufferTarget.ArrayBuffer, Vbo);
+
+            SetVertexFormat();
+        }
+
+        protected struct DrawCall
+        {
+            public PrimitiveType Type;
+            public int Start;
+            public int Count;
+            public TCall CallInfo;
+        }
+    }
 }