Use this file to discover all available pages before exploring further.
Prowl.Paper is distributed as a single NuGet package. This page covers every step from package installation to a renderer-ready project, including font setup and HiDPI configuration.
Prowl.Paper does not submit draw calls to a GPU directly. Instead, EndFrame produces a batched list of DrawCall objects that it hands off to an implementation of ICanvasRenderer. This design keeps the library completely decoupled from any specific graphics API.
The repository ships three reference renderer implementations. Pick the one that matches your graphics backend, copy it into your project, and adapt as needed.
The OpenTK renderer uses OpenGL 4 via the OpenTK.Graphics.OpenGL4 bindings. It manages VAOs, VBOs, an element buffer, a projection uniform, and an optional dual-Kawase backdrop-blur pipeline.
// From Samples/OpenTK/Program.csvar nativeWindowSettings = new NativeWindowSettings(){ ClientSize = new Vector2i(1080, 850), Title = "Paper OpenTK Example", Flags = OpenTK.Windowing.Common.ContextFlags.ForwardCompatible};using var app = new PaperTKWindow(GameWindowSettings.Default, nativeWindowSettings);app.Run();
Inside PaperTKWindow.OnLoad, the renderer is initialised and passed to Paper:
The full source is at Samples/OpenTK/PaperRenderer.cs.
Call PaperRenderer.UpdateProjection(width, height) whenever your window is resized so the orthographic projection matrix stays in sync with the framebuffer dimensions.
The Raylib renderer is the most compact backend — under 500 lines including the inline GLSL shader source. It integrates with Raylib’s Rlgl layer for direct vertex submission.
// From Samples/RaylibSample/Program.csint width = 1080;int height = 850;SetConfigFlags(ConfigFlags.ResizableWindow);InitWindow(width, height, "Raylib Sample");SetTargetFPS(60);_renderer = new RaylibCanvasRenderer();P = new Paper(_renderer, width, height, new Prowl.Quill.FontAtlasSettings());
The WASM backend wires Paper to a <canvas> element in the browser via JSExport interop. Initialisation, frame callbacks, and all input events are exported as JavaScript-callable methods.
// From Samples/WasmExample/Program.cs[JSExport]internal static void Init(){ WebGLInterop.InitWebGL("canvas"); _renderer = new WebGLCanvasRenderer(); var (cw, ch) = _renderer.GetCanvasSize(); P = new Paper(_renderer, cw, ch, new FontAtlasSettings()); Shared.PaperDemo.Initialize(P);}[JSExport]internal static void OnFrame(double deltaTime){ float dt = Math.Clamp((float)deltaTime, 0.001f, 0.1f); float dpiScale = (float)WebGLInterop.GetDevicePixelRatio(); P.BeginFrame(dt, dpiScale); Shared.PaperDemo.RenderUI(); P.EndFrame();}[JSExport]internal static void OnResize(int width, int height){ P.SetResolution(width, height);}
The full sample, including the JavaScript host page, is at Samples/WasmExample/.
If none of the provided backends fits your needs, implement ICanvasRenderer directly. The four members you must provide are:
Member
Responsibility
CreateTexture(width, height)
Allocate a GPU texture; return an opaque handle object.
GetTextureSize(texture)
Return the pixel dimensions of a previously created texture.
SetTextureData(texture, bounds, data)
Upload a byte region of RGBA pixel data to the texture.
RenderCalls(canvas, drawCalls)
Submit the frame’s batched draw calls to the GPU.
public class MyRenderer : ICanvasRenderer{ public bool SupportsBackdropBlur => false; // set true if you implement blur public object CreateTexture(uint width, uint height) { // allocate and return your GPU texture handle throw new NotImplementedException(); } public Int2 GetTextureSize(object texture) { throw new NotImplementedException(); } public void SetTextureData(object texture, IntRect bounds, byte[] data) { // upload RGBA data to the region described by bounds throw new NotImplementedException(); } public void RenderCalls(Canvas canvas, IReadOnlyList<DrawCall> drawCalls) { // iterate drawCalls; each has Vertices, Indices, Brush, Texture, Shader throw new NotImplementedException(); } public void Dispose() { // release GPU resources }}
Use canvas.Vertices and canvas.Indices to access the geometry, and drawCall.Brush for gradient/scissor/texture-transform uniforms. Refer to the OpenTK sample’s RenderCalls method for a fully annotated example.
Fonts are managed through FontAtlasSettings (passed to the Paper constructor) and registered at runtime via AddFallbackFont. FontFile is constructed from a stream pointing to a TTF or OTF file.
// 1. Create Paper with default atlas settingsvar paper = new Paper(renderer, width, height, new FontAtlasSettings());// 2. Load a TTF/OTF font from disk and register itusing var stream = File.OpenRead("path/to/font.ttf");var fontFile = new FontFile(stream);paper.AddFallbackFont(fontFile);// 3. Use the font in text declarationspaper.Box("Label") .Text(Text.Center("Hello", fontFile, Color.White));
You can enumerate system fonts to discover fonts available on the host OS:
// Enumerate fonts installed on the systemforeach (var font in paper.EnumerateSystemFonts()) Console.WriteLine(font.FamilyName);
Paper uses DisplayFramebufferScale to distinguish logical pixels from physical pixels. Set it before BeginFrame, or pass the DPI ratio as the optional second argument to BeginFrame:
// Option A: set the property directlypaper.DisplayFramebufferScale = new Float2(2.0f, 2.0f); // Retina / 2× displaypaper.BeginFrame(deltaTime);// Option B: pass dpiScale to BeginFrame (sets DisplayFramebufferScale internally)float dpiScale = GetRenderWidth() / (float)GetScreenWidth(); // Raylib examplepaper.BeginFrame(deltaTime, dpiScale);
For displays where you also want style values (padding, border widths, etc.) scaled up proportionally, call ScaleAllSizesonce at startup:
// Scale all registered default style values by the monitor's DPI ratiopaper.ScaleAllSizes(dpiRatio);
Call ScaleAllSizes only once during initialisation. Calling it repeatedly compounds the scale factor, which will produce oversized UI elements.
As Prowl.Paper is under active development, always pin to a specific version in your .csproj to avoid unexpected breaking changes between releases. Check the GitHub releases page for the latest version number.