Script
--original by idkwhattypehere
--edited by FEScriptzzBOII
local Players = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local HttpService = game:GetService("HttpService")
local ContentProvider = game:GetService("ContentProvider")
local RS = game:GetService("ReplicatedStorage")
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local MESH_ID = "rbxassetid://111891702759441"
local RED = Color3.fromRGB(255, 0, 4)
local BLACK = Color3.fromRGB(0, 0, 0)
local WHITE = Color3.fromRGB(255, 255, 255)
local GREEN = Color3.fromRGB(0, 255, 100)
local FONT = Enum.Font.TitilliumWeb
local env = (getgenv and getgenv()) or _G
if playerGui:FindFirstChild("F3XSkyboxStudio") then
playerGui.F3XSkyboxStudio:Destroy()
end
local S = {
part = nil,
texture = "",
scale = 1000,
spinOn = false,
spin = Vector3.new(0, 20, 0), -- deg/sec per axis
rot = Vector3.new(0, 0, 0),
vortex = false,
vortexPower = 1,
pulse = 6, -- % scale pulse in vortex
fade = true, -- vortex: smooth colour crossfade (false = hard disco cuts)
fadeTime = 1.5, -- seconds per colour
follow = false,
lock = true,
strict = false, -- Fork3X / strict mode
spinSpeed = 1, -- master multiplier for the spin sliders
light = false, -- vortex light: brightness boost on the mesh
bright = 1.5,
volume = 5, -- HD Admin ;volume value
pitch = 1, -- HD Admin ;pitch value
hdPrefix = ";",
pos = Vector3.new(0, 0, 0),
anim = { on = false, fps = 4, loop = true, frames = {}, idx = 0, last = 0, current = nil },
busy = false,
}
local sent = {} -- last values pushed to the server
local screen = Instance.new("ScreenGui")
screen.Name = "F3XSkyboxStudio"
screen.ResetOnSpawn = false
screen.Parent = playerGui
local function new(class, props, parent)
local o = Instance.new(class)
for k, v in pairs(props) do o[k] = v end
o.Parent = parent
return o
end
local function box(props, parent)
props.BackgroundColor3 = props.BackgroundColor3 or BLACK
props.BorderColor3 = RED
props.BorderSizePixel = 1
return new("Frame", props, parent)
end
local function label(parent, text, pos, size, align)
return new("TextLabel", {
Text = text, Position = pos, Size = size, BackgroundTransparency = 1,
TextColor3 = WHITE, TextScaled = true, Font = FONT,
TextXAlignment = align or Enum.TextXAlignment.Left,
}, parent)
end
local function button(parent, text, pos, size, cb)
local b = new("TextButton", {
Text = text, Position = pos, Size = size, BackgroundColor3 = BLACK,
BorderColor3 = RED, BorderSizePixel = 1, TextColor3 = WHITE,
TextScaled = true, Font = FONT, AutoButtonColor = false,
}, parent)
b.MouseEnter:Connect(function() b.BackgroundColor3 = Color3.fromRGB(40, 0, 0) end)
b.MouseLeave:Connect(function() b.BackgroundColor3 = BLACK end)
if cb then b.MouseButton1Click:Connect(cb) end
return b
end
local function input(parent, placeholder, pos, size)
return new("TextBox", {
Text = "", PlaceholderText = placeholder, PlaceholderColor3 = Color3.fromRGB(150, 150, 150),
Position = pos, Size = size, BackgroundColor3 = WHITE, BorderColor3 = RED,
BorderSizePixel = 1, TextColor3 = BLACK, TextScaled = true, Font = FONT,
ClearTextOnFocus = false,
}, parent)
end
local function toggle(parent, pos, size, text, init, cb)
local state = init
local b = button(parent, "", pos, size)
local function refresh()
b.Text = text .. ": " .. (state and "ON" or "OFF")
b.TextColor3 = state and GREEN or WHITE
end
b.MouseButton1Click:Connect(function()
state = not state
refresh()
cb(state)
end)
refresh()
return function(v) state = v; refresh() end
end
local function sliderRow(parent, y, name, min, max, init, cb)
local current = init
label(parent, name, UDim2.new(0, 4, 0, y), UDim2.new(0, 34, 0, 18))
local bar = new("Frame", {
Size = UDim2.new(1, -100, 0, 6), Position = UDim2.new(0, 40, 0, y + 6),
BackgroundColor3 = Color3.fromRGB(45, 45, 45), BorderSizePixel = 0,
}, parent)
local fill = new("Frame", { Size = UDim2.new(0, 0, 1, 0), BackgroundColor3 = RED, BorderSizePixel = 0 }, bar)
local hit = new("TextButton", {
Text = "", BackgroundTransparency = 1, Size = UDim2.new(1, 0, 0, 18), Position = UDim2.new(0, 0, 0.5, -9),
}, bar)
local tb = input(parent, "", UDim2.new(1, -56, 0, y), UDim2.new(0, 52, 0, 18))
local function show(v)
current = v
tb.Text = tostring(math.floor(v * 100 + 0.5) / 100)
fill.Size = UDim2.new(math.clamp((v - min) / (max - min), 0, 1), 0, 1, 0)
end
local function set(v)
show(v)
cb(v)
end
local dragging = false
local function fromX(x)
local a = math.clamp((x - bar.AbsolutePosition.X) / bar.AbsoluteSize.X, 0, 1)
set(min + (max - min) * a)
end
hit.InputBegan:Connect(function(i)
if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then
dragging = true
fromX(i.Position.X)
end
end)
UIS.InputChanged:Connect(function(i)
if dragging and (i.UserInputType == Enum.UserInputType.MouseMovement or i.UserInputType == Enum.UserInputType.Touch) then
fromX(i.Position.X)
end
end)
UIS.InputEnded:Connect(function(i)
if i.UserInputType == Enum.UserInputType.MouseButton1 or i.UserInputType == Enum.UserInputType.Touch then
dragging = false
end
end)
tb.FocusLost:Connect(function()
local n = tonumber(tb.Text)
if n then set(n) else show(current) end
end)
show(init)
return function(v) show(v); cb(v) end
end
local function makeWindow(titleText, pos, size, closable)
local frame = box({ Size = size, Position = pos, Active = true, Draggable = true }, screen)
local bar = new("TextLabel", {
Size = UDim2.new(1, 0, 0, 16), BackgroundColor3 = BLACK, BorderColor3 = RED, BorderSizePixel = 1,
Text = titleText, TextColor3 = WHITE, TextScaled = true, Font = FONT,
}, frame)
local body = new("Frame", {
Size = UDim2.new(1, 0, 1, -16), Position = UDim2.new(0, 0, 0, 16), BackgroundTransparency = 1,
}, frame)
local minimized = false
local minBtn = button(frame, "-", UDim2.new(1, closable and -34 or -17, 0, 0), UDim2.new(0, 16, 0, 16))
minBtn.ZIndex = 3
minBtn.MouseButton1Click:Connect(function()
minimized = not minimized
body.Visible = not minimized
minBtn.Text = minimized and "+" or "-"
frame.Size = minimized and UDim2.new(size.X.Scale, size.X.Offset, 0, 16) or size
end)
if closable then
local x = button(frame, "x", UDim2.new(1, -17, 0, 0), UDim2.new(0, 16, 0, 16), function() frame.Visible = false end)
x.ZIndex = 3
end
return frame, body
end
local cachedEndpoint
local statusLabel
local function status(text)
if statusLabel then statusLabel.Text = text end
end
local function scan(root)
if not root then return nil end
for _, o in ipairs(root:GetDescendants()) do
if o.Name == "SyncAPI" then
local ep = o:FindFirstChild("ServerEndpoint")
if ep then return ep end
end
end
end
local function findEndpoint()
if cachedEndpoint and cachedEndpoint.Parent then return cachedEndpoint end
cachedEndpoint = scan(player) or scan(player.Character) or scan(RS)
if not cachedEndpoint then -- fork fallback: any RemoteFunction called ServerEndpoint
for _, root in ipairs({ player, player.Character, RS }) do
if root then
for _, o in ipairs(root:GetDescendants()) do
if o:IsA("RemoteFunction") and o.Name == "ServerEndpoint" then
cachedEndpoint = o
break
end
end
end
if cachedEndpoint then break end
end
end
return cachedEndpoint
end
local function call(...)
local ep = findEndpoint()
if not ep then
status("Equip F3X first")
return false
end
local args = table.pack(...)
local tries = S.strict and 3 or 1
local ok, res
for _ = 1, tries do
ok, res = pcall(function() return ep:InvokeServer(table.unpack(args, 1, args.n)) end)
if ok then break end
task.wait(0.1)
end
if not ok then warn("[Skybox] " .. tostring(res)) end
if S.strict then task.wait(0.05) end
return ok, res
end
local function syncMesh(part, props)
if S.strict then -- strict: one property per request
for k, v in pairs(props) do
call("SyncMesh", { { Part = part, [k] = v } })
task.wait(0.08)
end
else
local e = { Part = part }
for k, v in pairs(props) do e[k] = v end
call("SyncMesh", { e })
end
end
local function assetUrl(id)
id = tostring(id or ""):gsub("%s", "")
if id == "" then return nil end
if id:match("^%d+$") then return "rbxassetid://" .. id end
return id
end
local texCache = {}
local function httpGet(url)
local req = env.request or env.http_request or (env.syn and env.syn.request) or (env.http and env.http.request)
if req then
local ok, res = pcall(req, { Url = url, Method = "GET" })
if ok and res and res.StatusCode == 200 then return res.Body end
end
local ok, body = pcall(function() return game:HttpGet(url) end)
if ok and type(body) == "string" then return body end
end
local function resolveTexture(text)
text = tostring(text or ""):gsub("%s", "")
if text == "" then return nil end
local num = text:match("^%d+$") or text:match("rbxassetid://(%d+)") or text:match("[?&]id=(%d+)")
if not num then return text end
if texCache[num] then return "rbxassetid://" .. texCache[num] end
status("Resolving " .. num .. "...")
local img
local ok, objs = pcall(function() return game:GetObjects("rbxassetid://" .. num) end)
if ok and type(objs) == "table" then
for _, o in ipairs(objs) do
local d = ((o:IsA("Decal") or o:IsA("Texture")) and o)
or o:FindFirstChildWhichIsA("Decal", true)
or o:FindFirstChildWhichIsA("Texture", true)
if d then
local t = tostring(d.Texture)
img = t:match("id=(%d+)") or t:match("rbxassetid://(%d+)")
end
o:Destroy()
if img then break end
end
end
if not img then
local body = httpGet("https://assetdelivery.roblox.com/v1/asset/?id=" .. num)
if body then
if body:sub(1, 30):find("<roblox") then
img = body:match("asset/%?id=(%d+)") or body:match("rbxassetid://(%d+)")
else
img = num -- raw image data, so it already is an image id
end
end
end
if img then
texCache[num] = img
texCache[img] = img
status(img == num and "Image id ok" or ("Decal " .. num .. " -> image " .. img))
return "rbxassetid://" .. img
end
status("Can't resolve decal here, use an image id")
return "rbxassetid://" .. num
end
local function hasSky()
return S.part ~= nil and S.part.Parent ~= nil
end
local function createSky()
if S.busy then return end
local url = assetUrl(S.texture)
if not url then status("Paste a texture id first") return end
local char = player.Character
local root = char and char:FindFirstChild("HumanoidRootPart")
if not root then status("No character") return end
S.busy = true
local cf = root.CFrame + Vector3.new(0, 6, 0)
local ok, part = call("CreatePart", "Normal", cf, workspace)
if not (ok and typeof(part) == "Instance") then
task.wait(0.2)
part = nil
for _, p in ipairs(workspace:GetChildren()) do
if p:IsA("BasePart") and (p.Position - cf.Position).Magnitude < 1 then part = p break end
end
end
if not part then
status("Create failed (F3X equipped?)")
S.busy = false
return
end
call("SetName", { part }, "Sky")
call("CreateMeshes", { { Part = part } })
task.wait(0.1)
local okM = call("SyncMesh", { { Part = part, MeshId = MESH_ID } })
local okT = call("SyncMesh", { { Part = part, TextureId = url } })
call("SyncMesh", { { Part = part, Scale = Vector3.new(S.scale, S.scale, S.scale) } })
call("SyncAnchor", { { Part = part, Anchored = true } })
call("SyncCollision", { { Part = part, CanCollide = false } }) -- no collisions
if S.lock then call("SetLocked", { part }, true) end
if not (okM and okT) then status("Mesh/texture request failed (see output)") end
S.part = part
S.pos = part.Position
S.rot = Vector3.new(0, 0, 0)
sent = { TextureId = url, Scale = Vector3.new(S.scale, S.scale, S.scale), VertexColor = Vector3.new(1, 1, 1), pos = S.pos }
S.busy = false
status("Skybox set")
end
local function deleteSky()
if not hasSky() then S.part = nil status("No skybox") return end
local part = S.part
S.part = nil
S.anim.on = false
call("SetLocked", { part }, false)
call("Remove", { part })
if part.Parent then call("Remove", { part }) end
status(part.Parent and "Delete may have failed" or "Skybox deleted")
end
local main, mainBody = makeWindow("F3x skybox", UDim2.new(0.45, 0, 0.3, 0), UDim2.new(0, 190, 0, 118), false)
local mainBox = input(mainBody, "paste ur texture id here", UDim2.new(0, 4, 0, 4), UDim2.new(1, -8, 0, 22))
local setBtn, delBtn, optBtn
setBtn = button(mainBody, "Set", UDim2.new(0, 4, 0, 30), UDim2.new(0.5, -6, 0, 22), function()
local url = resolveTexture(mainBox.Text)
if not url then status("Paste a texture id first") return end
S.texture = url
if hasSky() then
status("Updated")
else
createSky()
end
end)
delBtn = button(mainBody, "Delete", UDim2.new(0.5, 2, 0, 30), UDim2.new(0.5, -6, 0, 22), deleteSky)
optBtn = button(mainBody, "Options", UDim2.new(0, 4, 0, 56), UDim2.new(1, -8, 0, 22))
statusLabel = label(mainBody, "Equip F3X, paste id, Set", UDim2.new(0, 4, 0, 82), UDim2.new(1, -8, 0, 14))
-- live: editing the id updates an existing skybox after a short pause
local typeVer = 0
mainBox:GetPropertyChangedSignal("Text"):Connect(function()
typeVer += 1
local v = typeVer
task.delay(0.6, function()
if v == typeVer and hasSky() and mainBox.Text ~= "" then
S.texture = resolveTexture(mainBox.Text) or S.texture
end
end)
end)
local opt, optBody = makeWindow("Options", UDim2.new(0.45, 0, 0.3, 126), UDim2.new(0, 290, 0, 240), true)
opt.Visible = false
optBtn.MouseButton1Click:Connect(function() opt.Visible = not opt.Visible end)
local tabNames = { "Spin", "Vortex", "Size", "Music", "Anim", "Images", "Misc" }
local pages, tabBtns = {}, {}
local function showTab(name)
for n, p in pairs(pages) do p.Visible = (n == name) end
for n, b in pairs(tabBtns) do b.TextColor3 = (n == name) and RED or WHITE end
end
for i, n in ipairs(tabNames) do
tabBtns[n] = button(optBody, n, UDim2.new((i - 1) / #tabNames, 1, 0, 0), UDim2.new(1 / #tabNames, -2, 0, 18), function() showTab(n) end)
pages[n] = new("Frame", {
Size = UDim2.new(1, 0, 1, -22), Position = UDim2.new(0, 0, 0, 22), BackgroundTransparency = 1, Visible = false,
}, optBody)
end
do
local p = pages.Spin
local setT = toggle(p, UDim2.new(0, 4, 0, 0), UDim2.new(1, -8, 0, 24), "Spin skybox", S.spinOn, function(v) S.spinOn = v end)
sliderRow(p, 32, "Speed", 0, 10, S.spinSpeed, function(v) S.spinSpeed = v end)
sliderRow(p, 54, "X", -360, 360, S.spin.X, function(v) S.spin = Vector3.new(v, S.spin.Y, S.spin.Z) end)
sliderRow(p, 76, "Y", -360, 360, S.spin.Y, function(v) S.spin = Vector3.new(S.spin.X, v, S.spin.Z) end)
sliderRow(p, 98, "Z", -360, 360, S.spin.Z, function(v) S.spin = Vector3.new(S.spin.X, S.spin.Y, v) end)
label(p, "Speed multiplies X/Y/Z (deg/s). Type any number. Live.", UDim2.new(0, 4, 0, 124), UDim2.new(1, -8, 0, 14))
button(p, "Reset rotation", UDim2.new(0, 4, 0, 144), UDim2.new(1, -8, 0, 22), function() S.rot = Vector3.new(0, 0, 0) end)
end
do
local p = pages.Vortex
toggle(p, UDim2.new(0, 4, 0, 0), UDim2.new(1, -8, 0, 22), "Vortex disco", S.vortex, function(v) S.vortex = v end)
sliderRow(p, 26, "Power", 0.2, 3, S.vortexPower, function(v) S.vortexPower = v end)
sliderRow(p, 46, "Pulse", 0, 30, S.pulse, function(v) S.pulse = v end)
toggle(p, UDim2.new(0, 4, 0, 68), UDim2.new(1, -8, 0, 20), "Fade", S.fade, function(v) S.fade = v end)
sliderRow(p, 90, "Time", 0.2, 5, S.fadeTime, function(v) S.fadeTime = math.max(v, 0.1) end)
toggle(p, UDim2.new(0, 4, 0, 114), UDim2.new(1, -8, 0, 20), "Vortex light", S.light, function(v) S.light = v end)
sliderRow(p, 136, "Bright", 0, 5, S.bright, function(v) S.bright = v end)
label(p, "Light: skybox brightness (1 = normal, above 1 glows, 0 = black). Fade ON blends colours, OFF hard-cuts.", UDim2.new(0, 4, 0, 158), UDim2.new(1, -8, 0, 40))
end
do
local p = pages.Size
local setSize
setSize = sliderRow(p, 4, "Size", 10, 3000, S.scale, function(v) S.scale = math.max(v, 1) end)
label(p, "Type any number in the box, or drag the line.", UDim2.new(0, 4, 0, 28), UDim2.new(1, -8, 0, 14))
for i, v in ipairs({ 100, 500, 1000, 2000 }) do
button(p, tostring(v), UDim2.new((i - 1) / 4, 2, 0, 50), UDim2.new(0.25, -4, 0, 22), function() setSize(v) end)
end
end
local frameBoxes = {}
local setPlayBtn
do
local p = pages.Anim
for i = 1, 8 do
local col, rowN = (i - 1) % 4, math.floor((i - 1) / 4)
local tb = input(p, "frame " .. i, UDim2.new(col / 4, 2, 0, rowN * 26), UDim2.new(0.25, -4, 0, 22))
frameBoxes[i] = tb
end
sliderRow(p, 58, "FPS", 1, 30, S.anim.fps, function(v) S.anim.fps = math.max(v, 0.5) end)
local playBtn
local function refreshPlay()
playBtn.Text = S.anim.on and "Stop" or "Play"
playBtn.TextColor3 = S.anim.on and GREEN or WHITE
end
setPlayBtn = refreshPlay
playBtn = button(p, "Play", UDim2.new(0, 4, 0, 84), UDim2.new(0.5, -6, 0, 24), function()
if S.anim.on then
S.anim.on = false
else
if not hasSky() then status("Create the skybox first") return end
S.anim.frames = {}
local list = {}
for _, tb in ipairs(frameBoxes) do
local url = resolveTexture(tb.Text)
if url then
table.insert(S.anim.frames, url)
local d = Instance.new("Decal")
d.Texture = url
table.insert(list, d)
end
end
if #S.anim.frames < 2 then status("Add at least 2 frames") return end
task.spawn(function() -- preload so frames don't flicker for you
pcall(function() ContentProvider:PreloadAsync(list) end)
for _, d in ipairs(list) do d:Destroy() end
end)
S.anim.idx = 0
S.anim.last = 0
S.anim.on = true
end
refreshPlay()
end)
toggle(p, UDim2.new(0.5, 2, 0, 84), UDim2.new(0.5, -6, 0, 24), "Loop", S.anim.loop, function(v) S.anim.loop = v end)
button(p, "Clear frames", UDim2.new(0, 4, 0, 114), UDim2.new(1, -8, 0, 22), function()
for _, tb in ipairs(frameBoxes) do tb.Text = "" end
end)
end
do
local p = pages.Images
local searchBox = input(p, "search decals...", UDim2.new(0, 4, 0, 0), UDim2.new(1, -112, 0, 22))
local goBtn = button(p, "Go", UDim2.new(1, -106, 0, 0), UDim2.new(0, 36, 0, 22))
local mode = "sky"
local modeBtn = button(p, "-> Sky", UDim2.new(1, -66, 0, 0), UDim2.new(0, 62, 0, 22), function() end)
modeBtn.MouseButton1Click:Connect(function()
mode = (mode == "sky") and "frame" or "sky"
modeBtn.Text = (mode == "sky") and "-> Sky" or "-> Frame"
end)
local scroll = new("ScrollingFrame", {
Size = UDim2.new(1, -8, 0, 150), Position = UDim2.new(0, 4, 0, 26), BackgroundColor3 = Color3.fromRGB(15, 15, 15),
BorderColor3 = RED, BorderSizePixel = 1, ScrollBarThickness = 4, CanvasSize = UDim2.new(0, 0, 0, 0),
AutomaticCanvasSize = Enum.AutomaticSize.Y,
}, p)
new("UIGridLayout", { CellSize = UDim2.new(0, 60, 0, 60), CellPadding = UDim2.new(0, 4, 0, 4) }, scroll)
local moreBtn = button(p, "More", UDim2.new(0, 4, 0, 180), UDim2.new(1, -8, 0, 20))
local page, lastKw = 1, ""
local function pick(id)
task.spawn(function()
if mode == "sky" then
local url = resolveTexture(id) -- decal id -> real image id
S.texture = url
local img = url and url:match("%d+")
if img then mainBox.Text = img end
if not hasSky() then createSky() end
else
for _, tb in ipairs(frameBoxes) do
if tb.Text == "" then tb.Text = tostring(id) break end
end
end
end)
end
local function search(kw, pg)
local req = env.request or env.http_request or (env.syn and env.syn.request) or (env.http and env.http.request)
if not req then
status("No HTTP request function here")
return
end
local url = ("https://apis.roblox.com/toolbox-service/v1/marketplace/13?keyword=%s&limit=30&pageNumber=%d")
:format(HttpService:UrlEncode(kw), pg)
local ok, res = pcall(req, { Url = url, Method = "GET" })
if not (ok and res and res.StatusCode == 200) then
status("Search failed")
return
end
local okJ, data = pcall(HttpService.JSONDecode, HttpService, res.Body)
if not okJ or type(data) ~= "table" or type(data.data) ~= "table" then
status("Bad response")
return
end
if pg == 1 then
for _, c in ipairs(scroll:GetChildren()) do
if c:IsA("ImageButton") then c:Destroy() end
end
end
for _, d in ipairs(data.data) do
local id = d.id
if id then
local b = new("ImageButton", {
Image = ("rbxthumb://type=Asset&id=%d&w=150&h=150"):format(id),
BackgroundColor3 = Color3.fromRGB(30, 30, 30), BorderColor3 = RED, BorderSizePixel = 1,
}, scroll)
new("TextLabel", {
Size = UDim2.new(1, 0, 0, 12), Position = UDim2.new(0, 0, 1, -12), BackgroundColor3 = BLACK,
BackgroundTransparency = 0.3, Text = tostring(id), TextColor3 = WHITE, TextScaled = true, Font = FONT,
}, b)
b.MouseButton1Click:Connect(function() pick(id) end)
end
end
status(#data.data .. " results")
end
local function go()
lastKw = searchBox.Text
page = 1
search(lastKw, page)
end
goBtn.MouseButton1Click:Connect(go)
searchBox.FocusLost:Connect(function(enter) if enter then go() end end)
moreBtn.MouseButton1Click:Connect(function()
page += 1
search(lastKw, page)
end)
end
do
local p = pages.Misc
toggle(p, UDim2.new(0, 4, 0, 0), UDim2.new(1, -8, 0, 22), "Fork3X strict mode", S.strict, function(v) S.strict = v end)
toggle(p, UDim2.new(0, 4, 0, 26), UDim2.new(1, -8, 0, 22), "Lock part", S.lock, function(v)
S.lock = v
if hasSky() then call("SetLocked", { S.part }, v) end
end)
toggle(p, UDim2.new(0, 4, 0, 52), UDim2.new(1, -8, 0, 22), "Follow me", S.follow, function(v) S.follow = v end)
button(p, "Rescan F3X tool", UDim2.new(0, 4, 0, 78), UDim2.new(1, -8, 0, 22), function()
cachedEndpoint = nil
status(findEndpoint() and "F3X found" or "F3X not found")
end)
label(p, "Strict: one property per request, retries and delays for stricter servers.", UDim2.new(0, 4, 0, 104), UDim2.new(1, -8, 0, 28))
label(p, "HD prefix", UDim2.new(0, 4, 0, 138), UDim2.new(0, 60, 0, 20))
local pre = input(p, ";", UDim2.new(0, 68, 0, 138), UDim2.new(0, 40, 0, 20))
pre.Text = S.hdPrefix
pre.FocusLost:Connect(function()
if pre.Text ~= "" then S.hdPrefix = pre.Text end
end)
end
do
local p = pages.Music
local hdRemote
local function findHD()
if hdRemote and hdRemote.Parent then return hdRemote end
for _, root in ipairs({ RS, game }) do
for _, o in ipairs(root:GetDescendants()) do
if o.Name == "RequestCommandSilent" and (o:IsA("RemoteFunction") or o:IsA("RemoteEvent")) then
hdRemote = o
return o
end
end
end
end
local function hd(text)
local r = findHD()
if not r then
status("HD Admin remote not found")
return
end
task.spawn(function()
local ok, err = pcall(function()
if r:IsA("RemoteFunction") then return r:InvokeServer(text) end
r:FireServer(text)
end)
status(ok and ("Sent " .. text) or "HD request failed")
if not ok then warn("[Skybox] " .. tostring(err)) end
end)
end
local function cmd(name, arg)
return S.hdPrefix .. name .. (arg and (" " .. arg) or "")
end
local idBox = input(p, "music id", UDim2.new(0, 4, 0, 0), UDim2.new(1, -112, 0, 22))
local function play(id)
id = tostring(id or ""):match("%d+")
if not id then status("Enter a music id") return end
idBox.Text = id
hd(cmd("music", id))
end
button(p, "Play", UDim2.new(1, -106, 0, 0), UDim2.new(0, 50, 0, 22), function() play(idBox.Text) end)
button(p, "Stop", UDim2.new(1, -54, 0, 0), UDim2.new(0, 50, 0, 22), function() hd(cmd("stopmusic")) end)
-- volume: live, sent as ;volume <number> shortly after you stop dragging
local volVer = 0
sliderRow(p, 26, "Vol", 0, 10, S.volume, function(v)
S.volume = v
volVer += 1
local ver = volVer
task.delay(0.4, function()
if ver == volVer then hd(cmd("volume", tostring(math.floor(v * 100 + 0.5) / 100))) end
end)
end)
-- pitch: live, sent as ;pitch <number>
local pitchVer = 0
local setPitch
setPitch = sliderRow(p, 48, "Pitch", 0.1, 5, S.pitch, function(v)
if v < 0.1 then setPitch(0.1) return end -- minimum 0.1, no upper limit (type any number)
S.pitch = v
pitchVer += 1
local ver = pitchVer
task.delay(0.4, function()
if ver == pitchVer then hd(cmd("pitch", tostring(math.floor(v * 100 + 0.5) / 100))) end
end)
end)
local searchBox = input(p, "search store audio...", UDim2.new(0, 4, 0, 72), UDim2.new(1, -50, 0, 22))
local goBtn = button(p, "Go", UDim2.new(1, -44, 0, 72), UDim2.new(0, 40, 0, 22))
local list = new("ScrollingFrame", {
Size = UDim2.new(1, -8, 0, 76), Position = UDim2.new(0, 4, 0, 98), BackgroundColor3 = Color3.fromRGB(15, 15, 15),
BorderColor3 = RED, BorderSizePixel = 1, ScrollBarThickness = 4, CanvasSize = UDim2.new(0, 0, 0, 0),
AutomaticCanvasSize = Enum.AutomaticSize.Y,
}, p)
new("UIListLayout", { Padding = UDim.new(0, 2) }, list)
local moreBtn = button(p, "More", UDim2.new(0, 4, 0, 178), UDim2.new(1, -8, 0, 20))
local page, lastKw = 1, ""
local function search(kw, pg)
local url = ("https://apis.roblox.com/toolbox-service/v1/marketplace/3?keyword=%s&limit=30&pageNumber=%d")
:format(HttpService:UrlEncode(kw), pg)
local body = httpGet(url)
if not body then
status("Search failed (no HTTP?)")
return
end
local ok, data = pcall(HttpService.JSONDecode, HttpService, body)
if not ok or type(data) ~= "table" or type(data.data) ~= "table" then
status("Bad response")
return
end
local ids = {}
for _, d in ipairs(data.data) do
if d.id then table.insert(ids, d.id) end
end
if #ids == 0 then
status("No results")
return
end
local names = {}
local body2 = httpGet("https://apis.roblox.com/toolbox-service/v1/items/details?assetIds=" .. table.concat(ids, ","))
if body2 then
local ok2, det = pcall(HttpService.JSONDecode, HttpService, body2)
if ok2 and type(det) == "table" and type(det.data) == "table" then
for _, it in ipairs(det.data) do
local a = it.asset
if type(a) == "table" and a.id then names[a.id] = a.name end
end
end
end
if pg == 1 then
for _, c in ipairs(list:GetChildren()) do
if c:IsA("TextButton") then c:Destroy() end
end
end
for _, id in ipairs(ids) do
local b = button(list, (names[id] or "Audio") .. " [" .. id .. "]", UDim2.new(0, 0, 0, 0), UDim2.new(1, -6, 0, 18), function()
play(id)
end)
b.TextXAlignment = Enum.TextXAlignment.Left
end
status(#ids .. " tracks, click to play")
end
local function go()
lastKw = searchBox.Text
page = 1
search(lastKw, page)
end
goBtn.MouseButton1Click:Connect(go)
searchBox.FocusLost:Connect(function(enter) if enter then go() end end)
moreBtn.MouseButton1Click:Connect(function()
page += 1
search(lastKw, page)
end)
end
showTab("Spin")
local RunService = game:GetService("RunService")
local function angular(now)
local ang = Vector3.new(0, 0, 0)
if S.spinOn then ang += S.spin * S.spinSpeed end
if S.vortex then
local k = S.vortexPower
ang += Vector3.new(math.sin(now * 0.7) * 150 * k, 200 * k, math.cos(now * 0.9) * 150 * k)
end
return ang
end
local function hsvColor(i)
return Color3.fromHSV((i * 0.381966) % 1, 1, 1)
end
local function fx(now)
local scale = S.scale
local col = Vector3.new(1, 1, 1)
if S.vortex then
local k = S.vortexPower
scale = scale * (1 + (S.pulse / 100) * math.sin(now * 3 * k))
local t = now * k / math.max(S.fadeTime, 0.1)
local i = math.floor(t)
local a = t - i
local c = hsvColor(i)
if S.fade then
a = a * a * (3 - 2 * a)
c = c:Lerp(hsvColor(i + 1), a)
end
col = Vector3.new(c.R, c.G, c.B)
end
if S.light then col = col * S.bright end -- vortex light: >1 brightens, <1 dims
return Vector3.new(scale, scale, scale), col
end
local function cfFor(pos, rot)
return CFrame.new(pos) * CFrame.Angles(math.rad(rot.X), math.rad(rot.Y), math.rad(rot.Z))
end
local lastR = os.clock()
local renderConn = RunService.RenderStepped:Connect(function()
local now = os.clock()
local dt = math.min(now - lastR, 0.1)
lastR = now
local part = S.part
if not (part and part.Parent) then return end
if part.CanCollide then part.CanCollide = false end -- local guarantee, even if the server ignores it
S.rot += angular(now) * dt
if S.follow then
local char = player.Character
local root = char and char:FindFirstChild("HumanoidRootPart")
if root then S.pos = root.Position end
end
local cf = cfFor(S.pos, S.rot)
if part.CFrame ~= cf then part.CFrame = cf end
local mesh = part:FindFirstChildOfClass("SpecialMesh")
if mesh then
local sv, col = fx(now)
if mesh.Scale ~= sv then mesh.Scale = sv end
if mesh.VertexColor ~= col then mesh.VertexColor = col end
end
end)
screen.Destroying:Connect(function() renderConn:Disconnect() end)
local inflight = 0
local function fire(...)
if S.strict then
call(...)
return true
end
if inflight >= 4 then return false end
inflight += 1
local args = table.pack(...)
task.spawn(function()
call(table.unpack(args, 1, args.n))
inflight -= 1
end)
return true
end
task.spawn(function()
while screen.Parent do
local t0 = os.clock()
local part = S.part
if part and not part.Parent then
S.part = nil
S.anim.on = false
setPlayBtn()
status("Skybox was removed")
part = nil
end
if part and not S.busy then
-- animation frame advance
if S.anim.on and #S.anim.frames > 0 and t0 - S.anim.last >= 1 / S.anim.fps then
S.anim.last = t0
S.anim.idx += 1
if S.anim.idx > #S.anim.frames then
if S.anim.loop then
S.anim.idx = 1
else
S.anim.idx = #S.anim.frames
S.anim.on = false
setPlayBtn()
end
end
S.anim.current = S.anim.frames[S.anim.idx]
end
local url = (S.anim.on and S.anim.current) or assetUrl(S.texture)
if url and url ~= sent.TextureId then
syncMesh(part, { TextureId = url })
sent.TextureId = url
end
local sv, col = fx(t0)
local props = {}
if sent.Scale ~= sv then props.Scale = sv end
if sent.VertexColor ~= col then props.VertexColor = col end
if next(props) then
if S.strict then
syncMesh(part, props)
for k, v in pairs(props) do sent[k] = v end
else
local e = { Part = part }
for k, v in pairs(props) do e[k] = v end
if fire("SyncMesh", { e }) then
for k, v in pairs(props) do sent[k] = v end
end
end
end
local cf = cfFor(S.pos, S.rot)
if sent.CF ~= cf then
if fire("SyncMove", { { Part = part, CFrame = cf } }) then sent.CF = cf end
end
end
task.wait(math.max(0, 1 / 30 - (os.clock() - t0)))
end
end)Features
- make a skybox using f3x and its fully fe