Start typing to search packages!
screen-3d
By @raiven4ever
Roblox
MirroredScreen3D
Screen3D projects Roblox ScreenGui interfaces into 3D space. It lets you keep building UI with normal 2D GuiObjects, then opt individual objects into world-space projection through Component3D.
This repository is a fork of CatGuyMoment/Screen3D, originally introduced in the Roblox Developer Forum post Screen3D - A 3D UI framework that just works.
Purpose
Screen3D is the original framework: it owns the projection logic and defines how everything works.
This repo is a fork focused on packaging and polish. It wraps Screen3D as a typed Luau dependency for Wally, cleans up naming and formatting, documents the public API, and exports the Screen3D and Component3D types from the package root.
The original is the source of truth; this is just a better way to consume it.
What It Does
- Creates a
Screen3Dobject from aScreenGui - Indexes every
GuiObjectdescendant as aComponent3D - Converts selected 2D UI objects into
SurfaceGui-backed 3D UI - Supports rotating and moving projected UI through
CFrameoffsets - Supports nested projected UI objects
- Lets 2D and 3D UI objects coexist in the same hierarchy
- Lets you choose when projected components update
Installation
This package includes a Wally manifest:
[dependencies]
screen3d = "raiven4ever/screen-3d@0.1.0"
Install dependencies with Wally, then require the package from your project's package location.
Basic Usage
Create a Screen3D object from an existing ScreenGui:
local screen_gui: ScreenGui = path.to.your.ScreenGui
local Screen3D = require(path.to.Screen3D)
local screen_3d = Screen3D.new(screen_gui, 5)
The second argument is the display distance from the camera. Creating the Screen3D object indexes the GuiObject descendants, but it does not immediately convert them into 3D UI.
To project a specific GuiObject, get its matching Component3D and enable it:
local frame = screen_gui:WaitForChild("Frame") :: GuiObject
local component_3d = screen_3d:GetComponent3D(frame)
if component_3d then
component_3d:Enable()
component_3d:Update()
end
Component3D:Enable() moves the GuiObject into a SurfaceGui. Component3D:Update() refreshes its canvas size, backing part size, and world-space transform.
Unlike upstream, components do not create their own RenderStepped connections. To match the original automatic behavior, call Update() from one shared RenderStepped connection:
local RunService = game:GetService("RunService")
RunService.RenderStepped:Connect(function()
if component_3d then
component_3d:Update()
end
end)
Use Component3D:Disable() to stop projection and restore the object to its 2D parent.
Offsets
Each Component3D has an Offset property. The offset is applied around the UI object's anchor point and can rotate or move the projected object relative to its parent.
local frame = screen_gui:WaitForChild("Frame") :: GuiObject
local component_3d = screen_3d:GetComponent3D(frame)
if component_3d then
component_3d:Enable()
component_3d.Offset = CFrame.Angles(0, math.rad(10), 0)
component_3d:Update()
end
Offsets can be changed continuously:
local RunService = game:GetService("RunService")
RunService.RenderStepped:Connect(function()
if component_3d then
component_3d.Offset = CFrame.Angles(0, math.sin(os.clock()) / 2, 0)
component_3d:Update()
end
end)
You can combine position and rotation to create an indented or angled panel:
if component_3d then
component_3d.Offset = CFrame.new(0, 0, -0.1) * CFrame.Angles(0, math.rad(-6), 0)
component_3d:Update()
end
Nesting
Projected components can be nested. A child Component3D follows the transform of its projected parent while keeping its own Offset.
local frame = screen_gui:WaitForChild("Frame") :: GuiObject
local inner = frame:WaitForChild("Inner") :: GuiObject
local frame_3d = screen_3d:GetComponent3D(frame)
local inner_3d = screen_3d:GetComponent3D(inner)
if frame_3d and inner_3d then
frame_3d:Enable()
inner_3d:Enable()
inner_3d.Offset = CFrame.Angles(0, math.rad(25), 0)
frame_3d:Update()
inner_3d:Update()
end
You do not need to enable an entire UI tree just to rotate one object. 2D UI can contain 3D components, and 3D components can contain more projected children.
API
The package root returns the Screen3D class. Public Luau types are re-exported from init.lua and are defined in Types.lua.
local Screen3D = require(path.to.Screen3D)
type Screen3D = Screen3D.Screen3D
type Component3D = Screen3D.Component3D
Screen3D
local screen_3d = Screen3D.new(screen_gui, display_distance)
Screen3D.new(screen_gui, display_distance)creates projection state for aScreenGuiscreen_3d:GetComponent3D(gui_object)returns the indexedComponent3D, ornilscreen_3d:GetRealCanvasSize()returns the camera viewport sizescreen_3d:GetInset()returns the GUI insetscreen_3d:GetInsetCanvasSize()returns viewport size minus GUI insetscreen_3d:GetIntendedCanvasSize()respectsScreenGui.IgnoreGuiInset
Component3D
component_3d:Enable()starts projectioncomponent_3d:Update()refreshes the projected canvas size, part size, and transformcomponent_3d:Disable()stops projectioncomponent_3d:EnableCompatibility()enables wrapper-frame compatibility behavior for nested layoutscomponent_3d.Offsetcontrols local projected rotation and positioncomponent_3d:GetViewportSize()returns the active canvas size for the componentcomponent_3d:ReadWorldCFrame()computes the current world-space transform
Notes
- Projection is opt-in for performance: creating
Screen3Donly indexes objects. - Projection updates are manual: use one shared
RenderSteppedconnection when you want continuous tracking. - Manual updates avoid creating one
RenderSteppedconnection per enabled component. Offsetpivots around the original UI object'sAnchorPoint.- For angled corner panels, set the UI object's
AnchorPointto the pivot you want before enabling projection. - True curved GUI is not provided by this module; the original forum thread discusses Roblox engine limitations around curvature.
Differences From The Original Files
This fork is based on the original author's Component3D.luau, Definitions.luau, and init.luau, with manual update behavior inspired by Contrastual/Screen3D.
Main differences:
- Files were renamed from
.luauto.lua. - The shared type definitions live in
Types.lua, replacing the originalDefinitions.luau. - Types were rewritten as plain exported object-shape types and documented there.
- Class/table names and public fields use PascalCase conventions.
- Formatting and comments were cleaned up.
Component3Dobjects update through explicitComponent3D:Update()calls instead of one automaticRenderSteppedconnection per component.- A duplicate
GetStudsScreenSizecall inUDim2ToCFramewas removed. - Wally metadata, licensing, and this README were added for packaging.
Credits
Original project: CatGuyMoment/Screen3D
Original DevForum resource: Screen3D - A 3D UI framework that just works
Manual update behavior inspired by Contrastual/Screen3D.
This fork is maintained under raiven4ever/screen-3d.
Package Details
Install command (Click to copy)
Version
0.1.1
License
Unlicense
Safe for commercial use
Automated license review — not legal advice.
