
KeylessUniversal

--[[
Keyboard Layout v3
- 10 layout slots, named
- Tap a slot name -> load + lock (no dragging)
- Tap ✎ -> load + open edit mode (drag + save)
- Tap ✕ (x2) -> delete slot
- + CREATE NEW / BUILD WITHOUT SAVING
--]]
task.wait(7)
local Players = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local VIM = game:GetService("VirtualInputManager")
local PPromptSvc = game:GetService("ProximityPromptService")
local HttpSvc = game:GetService("HttpService")
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local isMobile = UIS.TouchEnabled and not UIS.KeyboardEnabled
pcall(function() UIS.MouseEnabled = true end)
pcall(function() UIS.KeyboardEnabled = true end)
----------------------------------------------------------------
-- CONFIG
----------------------------------------------------------------
local CONFIG = {
KeySize = isMobile and 72 or 48,
KeySpacing = isMobile and 8 or 6,
CornerR = isMobile and 12 or 8,
BaseColor = Color3.fromRGB(30, 30, 36),
PressedColor = Color3.fromRGB(90, 160, 255),
ActiveColor = Color3.fromRGB(80, 220, 130),
TextColor = Color3.fromRGB(235, 235, 245),
KeyFont = Enum.Font.GothamBold,
TextSize = isMobile and 26 or 18,
PanelColor = Color3.fromRGB(20, 20, 26),
PanelAccent = Color3.fromRGB(70, 130, 200),
PanelGreen = Color3.fromRGB(60, 180, 100),
PanelRed = Color3.fromRGB(180, 60, 60),
}
local WASD_POS = isMobile and UDim2.fromScale(0.20, 0.72) or UDim2.fromScale(0.5, 0.83)
local SPACE_POS = isMobile and UDim2.fromScale(0.80, 0.68) or UDim2.fromScale(0.5, 0.92)
local SHIFT_POS = isMobile and UDim2.fromScale(0.80, 0.85) or UDim2.fromScale(0.62, 0.92)
----------------------------------------------------------------
-- ADDABLE KEYS
----------------------------------------------------------------
local ADDABLE_KEYS = {
{label="E",code=Enum.KeyCode.E}, {label="Q",code=Enum.KeyCode.Q},
{label="R",code=Enum.KeyCode.R}, {label="F",code=Enum.KeyCode.F},
{label="G",code=Enum.KeyCode.G}, {label="C",code=Enum.KeyCode.C},
{label="V",code=Enum.KeyCode.V}, {label="X",code=Enum.KeyCode.X},
{label="Z",code=Enum.KeyCode.Z}, {label="T",code=Enum.KeyCode.T},
{label="Y",code=Enum.KeyCode.Y}, {label="H",code=Enum.KeyCode.H},
{label="B",code=Enum.KeyCode.B}, {label="N",code=Enum.KeyCode.N},
{label="M",code=Enum.KeyCode.M}, {label="J",code=Enum.KeyCode.J},
{label="K",code=Enum.KeyCode.K}, {label="L",code=Enum.KeyCode.L},
{label="P",code=Enum.KeyCode.P}, {label="O",code=Enum.KeyCode.O},
{label="I",code=Enum.KeyCode.I}, {label="U",code=Enum.KeyCode.U},
{label="1",code=Enum.KeyCode.One}, {label="2",code=Enum.KeyCode.Two},
{label="3",code=Enum.KeyCode.Three},{label="4",code=Enum.KeyCode.Four},
{label="5",code=Enum.KeyCode.Five},
{label="LMB",code=Enum.UserInputType.MouseButton1,isMouse=true},
{label="RMB",code=Enum.UserInputType.MouseButton2,isMouse=true},
{label="L-Ctrl",code=Enum.KeyCode.LeftControl},
{label="R-Ctrl",code=Enum.KeyCode.RightControl},
{label="Alt",code=Enum.KeyCode.LeftAlt},
{label="Tab",code=Enum.KeyCode.Tab},
{label="Esc",code=Enum.KeyCode.Escape},
{label="Enter",code=Enum.KeyCode.Return},
}
----------------------------------------------------------------
-- PERSISTENCE
----------------------------------------------------------------
local FILE_PATH = "kb_layouts_v2.json"
local layoutsData = {}
local function loadLayouts()
local ok, content = pcall(function()
if isfile and isfile(FILE_PATH) then return readfile(FILE_PATH) end
end)
if ok and content then
local ok2, data = pcall(function() return HttpSvc:JSONDecode(content) end)
if ok2 and type(data) == "table" then layoutsData = data end
end
for i = 1, 10 do
if layoutsData[i] == nil then layoutsData[i] = false end
end
end
local function saveLayouts()
pcall(function()
if writefile then
writefile(FILE_PATH, HttpSvc:JSONEncode(layoutsData))
end
end)
end
loadLayouts()
----------------------------------------------------------------
-- DISABLE DEFAULT MOBILE CONTROLS
----------------------------------------------------------------
if isMobile then
task.spawn(function()
local ps = player:WaitForChild("PlayerScripts")
local mod = require(ps:WaitForChild("PlayerModule"))
mod:GetControls():Disable()
end)
task.spawn(function()
local touchGui = playerGui:WaitForChild("TouchGui", 5)
if touchGui then touchGui.Enabled = false end
end)
end
----------------------------------------------------------------
-- ROOT GUI
----------------------------------------------------------------
local gui = Instance.new("ScreenGui")
gui.Name = "KeyboardLayout"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.DisplayOrder = 100
gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
gui.Parent = playerGui
local K, S = CONFIG.KeySize, CONFIG.KeySpacing
----------------------------------------------------------------
-- STATE
----------------------------------------------------------------
local held = { W=false, A=false, S=false, D=false, Space=false }
local shiftLockOn = false
local setupMode = true
local dragState = nil
local pressState = {}
local containers = {}
local activeLayoutIndex = nil
local containersById = {}
----------------------------------------------------------------
-- HELPERS
----------------------------------------------------------------
local function makeContainer(pos, w, h)
local c = Instance.new("Frame")
c.BackgroundTransparency = 1
c.AnchorPoint = Vector2.new(0.5, 0.5)
c.Position = pos
c.Size = UDim2.fromOffset(w, h)
c.Parent = gui
containers[c] = true
return c
end
local function makeKey(parent, text, posX, posY, width)
local key = Instance.new("TextButton")
key.Text = text
key.TextColor3 = CONFIG.TextColor
key.Font = CONFIG.KeyFont
key.TextSize = CONFIG.TextSize
key.AutoButtonColor = false
key.BackgroundColor3 = CONFIG.BaseColor
key.BorderSizePixel = 0
key.ZIndex = 2
key.Selectable = false
key.Active = true
key.Size = UDim2.fromOffset(width or K, K)
key.Position = UDim2.fromOffset(posX, posY)
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, CONFIG.CornerR)
corner.Parent = key
local stroke = Instance.new("UIStroke")
stroke.Color = Color3.fromRGB(60, 60, 70)
stroke.Thickness = 1
stroke.Parent = key
key.Parent = parent
return key
end
local function udim2ToTable(u)
return {u.X.Scale, u.X.Offset, u.Y.Scale, u.Y.Offset}
end
local function tableToUdim2(t)
if type(t) ~= "table" or #t < 4 then return UDim2.fromScale(0.5, 0.5) end
return UDim2.new(t[1], t[2], t[3], t[4])
end
----------------------------------------------------------------
-- PROMPT HANDLING
----------------------------------------------------------------
local activePrompts = {}
local heldPrompts = {}
local function forceCustomStyle(prompt)
pcall(function()
if prompt.Style ~= Enum.ProximityPromptStyle.Custom then
prompt.Style = Enum.ProximityPromptStyle.Custom
end
end)
end
local function makePromptBillboard(prompt)
local target = prompt.Parent
if not target or not target:IsA("BasePart") then return nil end
local bb = Instance.new("BillboardGui")
bb.Name = "KBLayoutPromptHint"
bb.Size = UDim2.fromOffset(110, 36)
bb.StudsOffset = Vector3.new(0, 2.5, 0)
bb.AlwaysOnTop = true
bb.MaxDistance = prompt.MaxActivationDistance or 10
bb.Parent = target
local frame = Instance.new("Frame")
frame.Size = UDim2.fromScale(1, 1)
frame.BackgroundColor3 = Color3.fromRGB(20, 20, 26)
frame.BackgroundTransparency = 0.15
frame.BorderSizePixel = 0
frame.Parent = bb
local fc = Instance.new("UICorner")
fc.CornerRadius = UDim.new(0, 8)
fc.Parent = frame
local label = Instance.new("TextLabel")
label.Size = UDim2.fromScale(1, 1)
label.BackgroundTransparency = 1
label.TextColor3 = Color3.fromRGB(240, 240, 250)
label.Font = Enum.Font.GothamBold
label.TextSize = 16
label.Text = string.format("[%s] %s",
prompt.KeyboardKeyCode.Name,
prompt.ActionText ~= "" and prompt.ActionText or "Interact")
label.Parent = frame
return bb
end
for _, d in ipairs(game:GetDescendants()) do
if d:IsA("ProximityPrompt") then forceCustomStyle(d) end
end
game.DescendantAdded:Connect(function(d)
if d:IsA("ProximityPrompt") then forceCustomStyle(d) end
end)
PPromptSvc.PromptShown:Connect(function(prompt)
forceCustomStyle(prompt)
local bb = makePromptBillboard(prompt)
activePrompts[prompt] = {billboard = bb}
end)
PPromptSvc.PromptHidden:Connect(function(prompt)
local data = activePrompts[prompt]
if data and data.billboard then data.billboard:Destroy() end
activePrompts[prompt] = nil
heldPrompts[prompt] = nil
end)
----------------------------------------------------------------
-- BUILD DEFAULT CLUSTERS
----------------------------------------------------------------
local wasdContainer = makeContainer(WASD_POS, K*3 + S*2, K*2 + S)
local spaceContainer = makeContainer(SPACE_POS, K, K)
local shiftContainer = makeContainer(SHIFT_POS, K, K)
containersById.wasd = wasdContainer
containersById.space = spaceContainer
containersById.shift = shiftContainer
local keyW = makeKey(wasdContainer, "W", 1*(K+S), 0)
local keyA = makeKey(wasdContainer, "A", 0, 1*(K+S))
local keyS = makeKey(wasdContainer, "S", 1*(K+S), 1*(K+S))
local keyD = makeKey(wasdContainer, "D", 2*(K+S), 1*(K+S))
local spaceKey = makeKey(spaceContainer, "⤒", 0, 0, K)
spaceKey.TextSize = isMobile and 40 or 24
local shiftKey = makeKey(shiftContainer, "🔒", 0, 0, K)
shiftKey.TextSize = isMobile and 32 or 20
----------------------------------------------------------------
-- ADDED KEYS
----------------------------------------------------------------
local addedKeys = {}
local function findAddedEntry(button)
for _, e in ipairs(addedKeys) do
if e.button == button then return e end
end
return nil
end
local function addKey(label, code, isMouse, position)
local c = makeContainer(position or UDim2.fromScale(0.5, 0.4), K, K)
local btn = makeKey(c, label, 0, 0, K)
if #label > 2 then
btn.TextSize = math.max(10, CONFIG.TextSize - 8)
end
local isInteract = (code == Enum.KeyCode.E or code == Enum.KeyCode.F) and not isMouse
local entry = {
container = c,
button = btn,
code = code,
isMouse = isMouse,
isInteract = isInteract,
}
table.insert(addedKeys, entry)
btn.InputBegan:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1
and input.UserInputType ~= Enum.UserInputType.Touch then return end
pressState[input] = { button = btn, id = "ADDED", wasDrag = false }
btn.BackgroundColor3 = CONFIG.PressedColor
if isInteract then
for prompt in pairs(activePrompts) do
if prompt.Parent and not heldPrompts[prompt] then
heldPrompts[prompt] = true
task.spawn(function()
pcall(function() prompt:InputHoldBegin() end)
end)
end
end
elseif not isMouse then
VIM:SendKeyEvent(true, code, false, game)
end
if setupMode and not dragState then
dragState = {
input = input,
container = c,
startMouse = input.Position,
startPos = c.Position,
moved = false,
}
end
end)
return entry
end
local function clearAddedKeys()
for _, e in ipairs(addedKeys) do
e.container:Destroy()
end
addedKeys = {}
end
----------------------------------------------------------------
-- LAYOUT SNAPSHOT / APPLY
----------------------------------------------------------------
local function snapshotLayout()
local snap = {containers = {}, addedKeys = {}}
for id, c in pairs(containersById) do
snap.containers[id] = udim2ToTable(c.Position)
end
for _, e in ipairs(addedKeys) do
table.insert(snap.addedKeys, {
label = e.button.Text,
codeName = e.code.Name,
isMouse = e.isMouse,
pos = udim2ToTable(e.container.Position),
})
end
return snap
end
local function applyLayout(layout)
if not layout then return end
for id, pos in pairs(layout.containers or {}) do
local c = containersById[id]
if c then c.Position = tableToUdim2(pos) end
end
clearAddedKeys()
for _, k in ipairs(layout.addedKeys or {}) do
local code
if k.isMouse then
code = Enum.UserInputType[k.codeName]
else
code = Enum.KeyCode[k.codeName]
end
if code then
addKey(k.label, code, k.isMouse, tableToUdim2(k.pos))
end
end
end
local function resetToDefault()
containersById.wasd.Position = WASD_POS
containersById.space.Position = SPACE_POS
containersById.shift.Position = SHIFT_POS
clearAddedKeys()
end
----------------------------------------------------------------
-- PRESS / RELEASE
----------------------------------------------------------------
local function cancelAllPresses()
for inputObj, entry in pairs(pressState) do
entry.button.BackgroundColor3 = CONFIG.BaseColor
if entry.id ~= "Shift" and held[entry.id] ~= nil then
held[entry.id] = false
end
pressState[inputObj] = nil
end
end
local function findContainerOf(button)
local p = button.Parent
while p and p ~= gui do
if containers[p] then return p end
p = p.Parent
end
return nil
end
local function bindKey(button, keyId)
button.InputBegan:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1
and input.UserInputType ~= Enum.UserInputType.Touch then return end
pressState[input] = { button = button, id = keyId, wasDrag = false }
button.BackgroundColor3 = CONFIG.PressedColor
if held[keyId] ~= nil and keyId ~= "Shift" then
held[keyId] = true
end
if setupMode and not dragState then
local c = findContainerOf(button)
if c then
dragState = {
input = input,
container = c,
startMouse = input.Position,
startPos = c.Position,
moved = false,
}
end
end
end)
end
bindKey(keyW, "W")
bindKey(keyA, "A")
bindKey(keyS, "S")
bindKey(keyD, "D")
bindKey(spaceKey, "Space")
bindKey(shiftKey, "Shift")
UIS.InputChanged:Connect(function(input)
if not dragState then return end
if input.UserInputType ~= Enum.UserInputType.MouseMovement
and input.UserInputType ~= Enum.UserInputType.Touch then return end
local delta = input.Position - dragState.startMouse
if not dragState.moved and delta.Magnitude > 8 then
dragState.moved = true
cancelAllPresses()
end
if dragState.moved and setupMode then
dragState.container.Position = UDim2.new(
dragState.startPos.X.Scale,
dragState.startPos.X.Offset + delta.X,
dragState.startPos.Y.Scale,
dragState.startPos.Y.Offset + delta.Y
)
end
end)
UIS.InputEnded:Connect(function(input)
local entry = pressState[input]
if entry then
local wasDrag = dragState and dragState.moved
entry.button.BackgroundColor3 = CONFIG.BaseColor
if entry.id == "Shift" then
if not wasDrag then
shiftLockOn = not shiftLockOn
shiftKey.BackgroundColor3 = shiftLockOn and CONFIG.ActiveColor or CONFIG.BaseColor
local char = player.Character
local hum = char and char:FindFirstChildOfClass("Humanoid")
if hum then
hum.CameraOffset = shiftLockOn and Vector3.new(1.75,0,0) or Vector3.zero
end
end
else
if held[entry.id] ~= nil then held[entry.id] = false end
local entryData = findAddedEntry(entry.button)
if entryData and not wasDrag then
if entryData.isInteract then
for prompt in pairs(heldPrompts) do
heldPrompts[prompt] = nil
task.spawn(function()
pcall(function() prompt:InputHoldEnd() end)
end)
end
elseif entryData.isMouse then
VIM:SendMouseButtonEvent(0, 0, entryData.code.Value, false, game, 0)
else
VIM:SendKeyEvent(false, entryData.code, false, game)
end
end
end
pressState[input] = nil
end
if dragState and input == dragState.input then
dragState = nil
end
end)
----------------------------------------------------------------
-- SETUP UI
----------------------------------------------------------------
local setupUI = Instance.new("Frame")
setupUI.Name = "SetupUI"
setupUI.BackgroundTransparency = 1
setupUI.Size = UDim2.new(1, 0, 0, 120)
setupUI.Position = UDim2.new(0, 0, 0, 0)
setupUI.ZIndex = 10
setupUI.Parent = gui
local hint = Instance.new("TextLabel")
hint.Text = "Drag to position · Tap + to add · Tap DONE to lock"
hint.Size = UDim2.new(0.9, 0, 0, 28)
hint.Position = UDim2.new(0.5, 0, 0, 15)
hint.AnchorPoint = Vector2.new(0.5, 0)
hint.BackgroundTransparency = 1
hint.TextColor3 = Color3.fromRGB(230, 230, 240)
hint.Font = Enum.Font.GothamBold
hint.TextSize = isMobile and 15 or 14
hint.ZIndex = 10
hint.Parent = setupUI
local function makeBtn(text, pos, color, parent)
local b = Instance.new("TextButton")
b.Text = text
b.Size = UDim2.fromOffset(130, 44)
b.Position = pos
b.AnchorPoint = Vector2.new(0.5, 0.5)
b.BackgroundColor3 = color
b.TextColor3 = Color3.fromRGB(255, 255, 255)
b.Font = Enum.Font.GothamBold
b.TextSize = 18
b.ZIndex = 11
b.Parent = parent or setupUI
local c = Instance.new("UICorner")
c.CornerRadius = UDim.new(0, 10)
c.Parent = b
return b
end
local addBtn = makeBtn("+ ADD", UDim2.new(0.5, -160, 0, 60), CONFIG.PanelAccent)
local saveBtn = makeBtn("SAVE", UDim2.new(0.5, 0, 0, 60), CONFIG.PanelAccent)
local doneBtn = makeBtn("DONE", UDim2.new(0.5, 160, 0, 60), CONFIG.PanelGreen)
----------------------------------------------------------------
-- KEY PICKER
----------------------------------------------------------------
local function openKeyPicker()
local picker = Instance.new("Frame")
picker.Size = UDim2.new(0.9, 0, 0.7, 0)
picker.Position = UDim2.fromScale(0.5, 0.5)
picker.AnchorPoint = Vector2.new(0.5, 0.5)
picker.BackgroundColor3 = CONFIG.PanelColor
picker.ZIndex = 20
picker.Parent = gui
local pc = Instance.new("UICorner")
pc.CornerRadius = UDim.new(0, 14)
pc.Parent = picker
local title = Instance.new("TextLabel")
title.Text = "Pick a key to add"
title.Size = UDim2.new(1, 0, 0, 40)
title.BackgroundTransparency = 1
title.TextColor3 = Color3.fromRGB(230, 230, 240)
title.Font = Enum.Font.GothamBold
title.TextSize = 18
title.ZIndex = 21
title.Parent = picker
local close = Instance.new("TextBNo comments yet for this script.



