Update to the latest version of OSGL here!
Note: OSGL currently does not work in live games, as it uses EditableImages. Until EditableImages release, this can only be used in studio.
What is OSGL?
OSGL (Open-Source Graphical Library) is designed to offer developers a straightforward and efficient way to read and write PixelData to EditableImages.
Originally created as an alternative to CanvasDraw, a graphics-library specifically built for Roblox, OSGL focuses exclusively on providing a robust framework for working with EditableImages.
Please note that OSGL is currently in a “beta” phase. While many features are planned, they are yet to be implemented. Your feedback and contributions are invaluable as we continue to enhance the library!
So, what can OSGL do?
OSGL provides users with a “Window”, also known as an EditableImage, that allows for drawing a variety of elements including shapes, textures, and text. Key features of OSGL include:
- Shapes: Allowing you to easily create and manipulate various geometric shapes.
- Textures: With support for a wide array of image formats!
- Built-in Error Handling: OSGL comes with its own error handler, catching and managing common (and annoying) EditableImage errors!
This library is a resource for developers looking to create pixel-based 2D or 3D games, as well as for projects that require efficient storage and manipulation of Pixel Data (for example, a drawing game!)
Examples
OSGL v1.2b and v1.3b currently have no examples.
OSGL v1.1b Examples
144P Shrek, sampled at 15FPS:
Minecraft Wireframe 3D Renderer (60FPS):
note: sadly I couldn’t find the RBXM file so I cannot record a video . But here are some images:
Live shading and rotating render:
Download OSGL
You can download the latest version from the github here, or from Roblox.
Quick tutorial
This tutorial assumes you have a basic understanding of Luau. If you’re new, you can find plenty of resources online!
You can learn more on the Docs.
The following tutorial will show you how to draw a circle to the screen.
Creating & Opening the window
Before we can start drawing on a window, we need to create one. Place the OSGL module somewhere, such as ReplicatedStorage/Packages
. Create an ImageLabel
in StarterGui
, with its BackgroundTransparency
set to 0. This ImageLabel
will serve as your canvas:
Lower resolutions cause blurred images! If you’re rendering at a low resolution, set the
ResampleMode
property of yourImageLabel
toPixelated
!
Additionally, create a LocalScript
in your desired location (e.g., StarterPlayer/StarterPlayerScripts
) and name it appropriately. This script will create our window and manage the rendering process.
OSGL is split into sub-modules that do different tasks. The sub-module we’re looking for is the Window
class, which handles creation of windows:
local OSGL = require(ReplicatedStorage.Packages.OSGL)
local Window = OSGL.Window
There are two functions available for creating our window: Window.from
and Window.new
. According to the API:
-
Window.from
: Creates an OSGL window from an existing EditableImage. -
Window.new
: Creates an OSGL window by initializing a new EditableImage instance at the specified location.
Since we don’t have an existing EditableImage
, we’ll use Window.new
to create our window on the designated ImageLabel
:
local OSGL = require(ReplicatedStorage.Packages.OSGL)
local Window = OSGL.Window
local windowUi = -- *reference to windowUi*
-- Create our window
local myWindow = Window.new(windowUi, { sizeX = 500, sizeY = 500 })
The example above creates an OSGL window, with a size of 500x500, on windowUi
. You can find more details about this function in the API.
Rendering to the Window
Great, now we have our window, but now we need to render to it, but only if it hasn’t been destroyed by some external script! OSGL provides a handy method, IsOpen
, that returns true while the window exists.
while myWindow:IsOpen() do
end
IsOpen
will automatically yield, so there’s no need for task.wait
here. We can focus directly on rendering!
To draw a circle, we will use the draw
sub-module, which allows us to render shapes directly to the provided double-buffer (meaning the rendering is not displayed on screen immediately). In addition, we need to use the color
module (which accepts RGBA values in the range of 0 to 255) to set a color for our window. We’ll use the default RED
color for this example:
local OSGL = require(ReplicatedStorage.Packages.OSGL)
local Window = OSGL.Window
local draw = OSGL.draw
local color = OSGL.color
local windowUi = -- *reference to windowUi*
-- Create our window
local myWindow = Window.new(windowUi, { sizeX = 500, sizeY = 500 })
-- Automatically yields for a heartbeat, so task.wait isn't needed!
while myWindow:IsOpen() do
draw.circle(myWindow, {
centerX = 250,
centerY = 250,
fillColor = color.RED,
radius = 200
-- Strokes are optional, so we don't need to
-- pass `strokeColor` and `strokeThickness`
})
end
The first argument for every drawing function should be your window. In this code, we create a circle centered at (250, 250) with a radius of 200 in RED
(255, 0, 0, 255).
After running this code, you may notice that nothing seems to happen (aside from a slight performance drop ). This is because we have only drawn to the buffer, and not yet to the screen. To display the contents of the buffer, we can use the Render
method of the Window
class:
local OSGL = require(ReplicatedStorage.Packages.OSGL)
local Window = OSGL.Window
local draw = OSGL.draw
local color = OSGL.color
local windowUi = -- *reference to windowUi*
-- Create our window
local myWindow = Window.new(windowUi, { sizeX = 500, sizeY = 500 })
-- Automatically yields for a heartbeat, so task.wait isn't needed!
while myWindow:IsOpen() do
draw.circle(myWindow, {
centerX = 250,
centerY = 250,
fillColor = color.RED,
radius = 200
-- Strokes are optional; no need to provide
-- strokeColor and strokeThickness
})
-- Renders the double-buffer to the screen
myWindow:Render()
end
Now everything should work beautifully! You have successfully rendered a circle in OSGL!
Full Code
--!optimize 2
-- ^^ This flag optimises
-- the script in studio
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local OSGL = require(ReplicatedStorage.Packages.OSGL)
local Window = OSGL.Window
local draw = OSGL.draw
local color = OSGL.color
local windowUi = -- *Reference to windowUi*
-- Create our 500x500 window
local myWindow = Window.new(windowUi, { sizeX = 500, sizeY = 500 })
-- While the window exists,
-- render every heartbeat.
while myWindow:IsOpen() do
-- Draw a circle at 250, 250, with a radius of 200, with a red color.
draw.circle(myWindow, {
centerX = 250,
centerY = 250,
fillColor = color.RED,
radius = 200
})
-- Render our circle to the screen.
myWindow:Render()
end
print("The window has been destroyed!")
Contribution
OSGL is an open-source project, and we welcome your contributions! You’re encouraged to edit the source code and share your ideas. Feel free to participate via GitHub or on the DevForum. Your involvement is greatly appreciated!
Credits
While you do not need to credit me for using OSGL, acknowledging the original creator is always appreciated but entirely optional. You are free to modify the source code as you wish, but please refrain from reuploading or claiming the asset as your own work.