
KeylessUniversal

-- FNF Recreation
--made by yourstraight_idk
local Players = game:GetService("Players")
local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
-- DEFAULT SETTINGS
local NOTE_SPEED = 380
local NOTE_COLOR = Color3.fromRGB(255,0,0)
local HIT_WINDOW = 55
local NOTE_SIZE = 48
local BUTTON_SIZE = 60
-- RANDOM DELAY SETTINGS
local MIN_DELAY = 0.4
local MAX_DELAY = 1.5
local MULTI_NOTE_DELAY = 0.18
-- GAME STATE
local score = 0
local combo = 0
local misses = 0
local notes = {}
local playing = false
local paused = false
local mode = "Easy"
local resumeTimer = 0
local isResuming = false
local spawnTimer = 0
local spawnDelay = math.random(MIN_DELAY * 100, MAX_DELAY * 100) / 100
local gameTime = 120
local timerRunning = false
local notesHit = 0
local notesSpawned = 0
-- HIT SOUND
local hitSoundId = ""
local hitSound = Instance.new("Sound")
hitSound.Parent = player:WaitForChild("PlayerGui")
hitSound.Volume = 1
-- GUI
local gui = Instance.new("ScreenGui")
gui.Parent = player.PlayerGui
gui.ResetOnSpawn = false
-- =========================
-- RESULTS UI
-- =========================
local resultFrame = Instance.new("Frame", gui)
resultFrame.Size = UDim2.fromScale(0.4,0.5)
resultFrame.Position = UDim2.fromScale(0.3,0.25)
resultFrame.BackgroundColor3 = Color3.fromRGB(20,20,20)
resultFrame.Visible = false
Instance.new("UICorner", resultFrame).CornerRadius = UDim.new(0,20)
local resultTitle = Instance.new("TextLabel", resultFrame)
resultTitle.Size = UDim2.fromScale(1,0.2)
resultTitle.Position = UDim2.fromScale(0,0)
resultTitle.BackgroundTransparency = 1
resultTitle.Text = "RESULTS"
resultTitle.TextScaled = true
resultTitle.TextColor3 = Color3.new(1,1,1)
local resultText = Instance.new("TextLabel", resultFrame)
resultText.Size = UDim2.fromScale(1,0.6)
resultText.Position = UDim2.fromScale(0,0.2)
resultText.BackgroundTransparency = 1
resultText.TextScaled = true
resultText.TextColor3 = Color3.new(1,1,1)
resultText.Text = ""
local replayBtn = Instance.new("TextButton", resultFrame)
replayBtn.Size = UDim2.fromScale(0.5,0.15)
replayBtn.Position = UDim2.fromScale(0.25,0.8)
replayBtn.Text = "REPLAY"
replayBtn.TextScaled = true
replayBtn.BackgroundColor3 = Color3.fromRGB(120,120,120)
-- =========================
-- MENU SCREEN
-- =========================
local menu = Instance.new("Frame", gui)
menu.Size = UDim2.fromScale(0.5,0.6)
menu.Position = UDim2.fromScale(0.25,0.2)
menu.BackgroundColor3 = Color3.fromRGB(20,20,20)
menu.Visible = false
Instance.new("UICorner", menu).CornerRadius = UDim.new(0,20)
local menuTitle = Instance.new("TextLabel", menu)
menuTitle.Size = UDim2.fromScale(1,0.25)
menuTitle.Position = UDim2.fromScale(0,0)
menuTitle.BackgroundTransparency = 1
menuTitle.Text = "RHYTHM GAME"
menuTitle.TextScaled = true
menuTitle.TextColor3 = Color3.new(1,1,1)
local playBtn = Instance.new("TextButton", menu)
playBtn.Size = UDim2.fromScale(0.6,0.15)
playBtn.Position = UDim2.fromScale(0.2,0.35)
playBtn.Text = "PLAY"
playBtn.TextScaled = true
playBtn.BackgroundColor3 = Color3.fromRGB(120,120,120)
local settingsBtn = Instance.new("TextButton", menu)
settingsBtn.Size = UDim2.fromScale(0.6,0.15)
settingsBtn.Position = UDim2.fromScale(0.2,0.55)
settingsBtn.Text = "SETTINGS"
settingsBtn.TextScaled = true
settingsBtn.BackgroundColor3 = Color3.fromRGB(120,120,120)
local menuBackBtn = Instance.new("TextButton", menu)
menuBackBtn.Size = UDim2.fromScale(0.6,0.15)
menuBackBtn.Position = UDim2.fromScale(0.2,0.75)
menuBackBtn.Text = "BACK TO GAME"
menuBackBtn.TextScaled = true
menuBackBtn.BackgroundColor3 = Color3.fromRGB(120,120,120)
-- =========================
-- SETTINGS SCREEN
-- =========================
local settings = Instance.new("Frame", gui)
settings.Size = UDim2.fromScale(0.5,0.6)
settings.Position = UDim2.fromScale(0.25,0.2)
settings.BackgroundColor3 = Color3.fromRGB(20,20,20)
settings.Visible = false
Instance.new("UICorner", settings).CornerRadius = UDim.new(0,20)
local settingsTitle = Instance.new("TextLabel", settings)
settingsTitle.Size = UDim2.fromScale(1,0.25)
settingsTitle.Position = UDim2.fromScale(0,0)
settingsTitle.BackgroundTransparency = 1
settingsTitle.Text = "SETTINGS"
settingsTitle.TextScaled = true
settingsTitle.TextColor3 = Color3.new(1,1,1)
-- Note Speed
local speedLabel = Instance.new("TextLabel", settings)
speedLabel.Size = UDim2.fromScale(0.9,0.12)
speedLabel.Position = UDim2.fromScale(0.05,0.3)
speedLabel.BackgroundTransparency = 1
speedLabel.Text = "Note Speed: "..NOTE_SPEED
speedLabel.TextScaled = true
speedLabel.TextColor3 = Color3.new(1,1,1)
local speedUp = Instance.new("TextButton", settings)
speedUp.Size = UDim2.fromScale(0.2,0.1)
speedUp.Position = UDim2.fromScale(0.7,0.42)
speedUp.Text = "+"
speedUp.TextScaled = true
speedUp.BackgroundColor3 = Color3.fromRGB(120,120,120)
local speedDown = Instance.new("TextButton", settings)
speedDown.Size = UDim2.fromScale(0.2,0.1)
speedDown.Position = UDim2.fromScale(0.1,0.42)
speedDown.Text = "-"
speedDown.TextScaled = true
speedDown.BackgroundColor3 = Color3.fromRGB(120,120,120)
speedUp.MouseButton1Click:Connect(function()
NOTE_SPEED += 20
speedLabel.Text = "Note Speed: "..NOTE_SPEED
end)
speedDown.MouseButton1Click:Connect(function()
if NOTE_SPEED > 100 then
NOTE_SPEED -= 20
speedLabel.Text = "Note Speed: "..NOTE_SPEED
end
end)
-- Note Color Buttons
local colorLabel = Instance.new("TextLabel", settings)
colorLabel.Size = UDim2.fromScale(0.9,0.12)
colorLabel.Position = UDim2.fromScale(0.05,0.55)
colorLabel.BackgroundTransparency = 1
colorLabel.Text = "Note Color"
colorLabel.TextScaled = true
colorLabel.TextColor3 = Color3.new(1,1,1)
local colors = {
{Color3.fromRGB(255,0,0), "RED"},
{Color3.fromRGB(0,255,0), "GREEN"},
{Color3.fromRGB(0,0,255), "BLUE"},
{Color3.fromRGB(255,255,0), "YELLOW"},
{Color3.fromRGB(255,0,255), "PINK"},
{Color3.fromRGB(0,255,255), "CYAN"},
{Color3.fromRGB(255,128,0), "ORANGE"},
{Color3.fromRGB(128,0,255), "PURPLE"}
}
for i, v in ipairs(colors) do
local btn = Instance.new("TextButton", settings)
btn.Size = UDim2.fromScale(0.2,0.1)
btn.Position = UDim2.fromScale(0.1 + ((i-1)%4)*0.3, 0.67 + math.floor((i-1)/4)*0.12)
btn.Text = v[2]
btn.TextScaled = true
btn.BackgroundColor3 = v[1]
btn.MouseButton1Click:Connect(function()
NOTE_COLOR = v[1]
end)
end
-- HIT SOUND SETTING
local soundLabel = Instance.new("TextLabel", settings)
soundLabel.Size = UDim2.fromScale(0.9,0.12)
soundLabel.Position = UDim2.fromScale(0.05,0.82)
soundLabel.BackgroundTransparency = 1
soundLabel.Text = "Hit Sound ID:"
soundLabel.TextScaled = true
soundLabel.TextColor3 = Color3.new(1,1,1)
local soundBox = Instance.new("TextBox", settings)
soundBox.Size = UDim2.fromScale(0.6,0.12)
soundBox.Position = UDim2.fromScale(0.05,0.92)
soundBox.PlaceholderText = "Paste Sound ID here"
soundBox.TextScaled = true
soundBox.TextColor3 = Color3.new(1,1,1)
soundBox.BackgroundColor3 = Color3.fromRGB(60,60,60)
local soundApply = Instance.new("TextButton", settings)
soundApply.Size = UDim2.fromScale(0.3,0.12)
soundApply.Position = UDim2.fromScale(0.68,0.92)
soundApply.Text = "Apply"
soundApply.TextScaled = true
soundApply.BackgroundColor3 = Color3.fromRGB(120,120,120)
soundApply.MouseButton1Click:Connect(function()
local id = soundBox.Text
if id ~= "" then
hitSoundId = id
hitSound.SoundId = "rbxassetid://" .. hitSoundId
end
end)
local backToMenu = Instance.new("TextButton", settings)
backToMenu.Size = UDim2.fromScale(0.6,0.15)
backToMenu.Position = UDim2.fromScale(0.2,0.85)
backToMenu.Text = "BACK TO MENU"
backToMenu.TextScaled = true
backToMenu.BackgroundColor3 = Color3.fromRGB(120,120,120)
backToMenu.MouseButton1Click:Connect(function()
settings.Visible = false
menu.Visible = true
end)
-- =========================
-- GAME SCREEN
-- =========================
local main = Instance.new("Frame", gui)
main.Size = UDim2.fromScale(0.45, 0.7)
main.Position = UDim2.fromScale(0.275, 0.15)
main.BackgroundColor3 = Color3.fromRGB(20,20,20)
main.Visible = false
Instance.new("UICorner", main).CornerRadius = UDim.new(0,20)
-- DRAGGABLE TOGGLE BUTTON
local toggle = Instance.new("TextButton", gui)
toggle.Size = UDim2.fromOffset(64,64)
toggle.Position = UDim2.fromScale(0.02,0.45)
toggle.Text = "🎵"
toggle.TextScaled = true
toggle.BackgroundColor3 = Color3.fromRGB(255,90,90)
toggle.Active = true
toggle.Draggable = true
Instance.new("UICorner", toggle).CornerRadius = UDim.new(1,0)
Instance.new("UIAspectRatioConstraint", toggle).AspectRatio = 1
-- TOGGLE BUTTON (ONLY HIDES/SHOWS GAME UI)
toggle.MouseButton1Click:Connect(function()
main.Visible = not main.Visible
menu.Visible = false
settings.Visible = false
resultFrame.Visible = false
-- START GAME IF SHOW BUTTON PRESS
if main.Visible and not playing then
playing = true
timerRunning = true
end
end)
-- INFO
local info = Instance.new("TextLabel", main)
info.Size = UDim2.fromScale(1,0.08)
info.BackgroundTransparency = 1
info.TextColor3 = Color3.new(1,1,1)
info.TextScaled = true
-- MODE LABEL
local modeLabel = Instance.new("TextLabel", main)
modeLabel.Size = UDim2.fromScale(1,0.08)
modeLabel.Position = UDim2.fromScale(0,0.08)
modeLabel.BackgroundTransparency = 1
modeLabel.TextColor3 = Color3.new(1,1,1)
modeLabel.TextScaled = true
modeLabel.Text = "Mode: Easy"
-- RESET FUNCTION (CALLED WHEN MODE CHANGES)
local function resetGame()
score = 0
combo = 0
misses = 0
notesHit = 0
notesSpawned = 0
gameTime = 120
timerRunning = true
paused = false
isResuming = false
for _, v in ipairs(notes) do
v.obj:Destroy()
end
notes = {}
end
-- START GAME FUNCTION
local function startGame()
playing = true
timerRunning = true
paused = false
isResuming = false
end
-- PAUSE BUTTON
local pauseBtn = Instance.new("TextButton", main)
pauseBtn.Size = UDim2.fromScale(0.2,0.08)
pauseBtn.Position = UDim2.fromScale(0.4,0.18)
pauseBtn.Text = "Pause"
pauseBtn.TextScaled = true
pauseBtn.BackgroundColor3 = Color3.fromRGB(120,120,120)
pauseBtn.MouseButton1Click:Connect(function()
if isResuming then return end
if paused then
isResuming = true
resumeTimer = 4
pauseBtn.Text = "Resuming..."
else
paused = true
pauseBtn.Text = "Resume"
for _, v in ipairs(notes) do
v.obj:Destroy()
end
notes = {}
end
end)
-- MODE BUTTONS
local function createModeButton(text, pos)
local btn = Instance.new("TextButton", main)
btn.Size = UDim2.fromScale(0.25,0.08)
btn.Position = pos
btn.Text = text
btn.TextScaled = true
btn.BackgroundColor3 = Color3.fromRGB(80,80,80)
btn.MouseButton1Click:Connect(function()
mode = text
modeLabel.Text = "Mode: "..text
resetGame()
end)
return btn
end
createModeButton("Easy", UDim2.fromScale(0.05,0.26))
createModeButton("Medium", UDim2.fromScale(0.375,0.26))
createModeButton("Hard", UDim2.fromScale(0.7,0.26))
createModeButton("Extreme", UDim2.fromScale(0.375,0.34))
-- MENU BUTTON
local menuBtn = Instance.new("TextButton", main)
menuBtn.Size = UDim2.fromScale(0.25,0.08)
menuBtn.Position = UDim2.fromScale(0.7,0.34)
menuBtn.Text = "MENU"
menuBtn.TextScaled = true
menuBtn.BackgroundColor3 = Color3.fromRGB(120,120,120)
menuBtn.MouseButton1Click:Connect(function()
menu.Visible = true
main.Visible = false
settings.Visible = false
resultFrame.Visible = false
end)
-- HIT LINE
local hitLine = Instance.new("Frame", main)
hitLine.Size = UDim2.fromScale(1,0.02)
hitLine.Position = UDim2.fromScale(0,0.75)
hitLine.BackgroundColor3 = Color3.new(1,1,1)
-- LANES + WASD BUTTONS
local lanes = {}
local keys = {"A","S","W","D"}
for i = 1,4 do
local lane = Instance.new("Frame", main)
lane.Size = UDim2.fromScale(0.25,1)
lane.Position = UDim2.fromScale((i-1)*0.25,0)
lane.BackgroundTransparency = 1
lanes[i] = lane
local btn = Instance.new("TextButton", main)
btn.Size = UDim2.fromOffset(BUTTON_SIZE,BUTTON_SIZE)
btn.Position = UDim2.fromScale((i-1)*0.25 + 0.125,0.9)
btn.AnchorPoint = Vector2.new(0.5,0.5)
btn.Text = keys[i]
btn.TextScaled = true
btn.TextColor3 = Color3.new(1,1,1)
btn.BackgroundColor3 = Color3.fromRGB(60,60,60)
Instance.new("UICorner", btn).CornerRadius = UDim.new(1,0)
Instance.new("UIAspectRatioConstraint", btn).AspectRatio = 1
btn.MouseButton1Click:Connect(function()
pressLane(i)
end)
end
-- SPAWN NOTE AT A HEIGHT
function spawnNote(laneIndex)
local note = Instance.new("Frame")
note.Size = UDim2.fromOffset(NOTE_SIZE,NOTE_SIZE)
-- SPAWN height
note.Position = UDim2.fromScale(0.5,-1)
note.AnchorPoint = Vector2.new(0.5,0)
note.BackgroundColor3 = NOTE_COLOR
note.Parent = lanes[laneIndex]
Instance.new("UICorner", note).CornerRadius = UDim.new(1,0)
Instance.new("UIAspectRatioConstraint", note).AspectRatio = 1
table.insert(notes,{
obj = note,
lane = laneIndex
})
notesSpawned += 1
end
-- SPAWN MULTIPLE NOTES WITH DELAY BETWEEN EACH NOTE
function spawnMultipleNotes()
local amount = 1
if mode == "Easy" then
amount = 1
elseif mode == "Medium" then
amount = 2
elseif mode == "Hard" then
amount = 3
elseif mode == "Extreme" then
amount = 4
end
local usedLanes = {}
for i = 1, amount do
local lane = math.random(1,4)
while usedLanes[lane] do
lane = math.random(1,4)
end
usedLanes[lane] = true
spawnNote(lane)
if i < amount then
wait(MULTI_NOTE_DELAY)
end
end
end
-- HIT CHECK (closest note)
function pressLane(lane)
if paused then return end
local closestNote = nil
local closestDist = math.huge
local closestIndex = nil
for i, data in ipairs(notes) do
if data.lane == lane then
local dy = math.abs(data.obj.AbsolutePosition.Y - hitLine.AbsolutePosition.Y)
if dy < closestDist then
closestDist = dy
closestNote = data.obj
closestIndex = i
end
end
end
if closestNote and closestDist <= HIT_WINDOW then
closestNote:Destroy()
table.remove(notes, closestIndex)
score += 100
combo += 1
notesHit += 1
-- PLAY HIT SOUND
if hitSoundId ~= "" then
hitSound:Play()
end
end
end
-- KEYBOARD INPUT
local keyMap = {
A = 1,
S = 2,
W = 3,
D = 4
}
UIS.InputBegan:Connect(function(input,gp)
if gp or not playing then return end
local lane = keyMap[input.KeyCode.Name]
if lane then
pressLane(lane)
end
end)
-- REPLAY BUTTON
replayBtn.MouseButton1Click:Connect(function()
resetGame()
resultFrame.Visible = false
main.Visible = true
menu.Visible = false
settings.Visible = false
playing = true
timerRunning = true
end)
-- MENU BACK BUTTON
menuBackBtn.MouseButton1Click:Connect(function()
menu.Visible = false
main.Visible = true
end)
-- PLAY BUTTON
playBtn.MouseButton1Click:Connect(function()
menu.Visible = false
main.Visible = true
startGame()
end)
settingsBtn.MouseButton1Click:Connect(function()
menu.Visible = false
settings.Visible = true
end)
-- GAME LOOP
RunService.RenderStepped:Connect(function(dt)
info.Text = "Score: "..score.." | Combo: "..combo.." | Misses: "..misses .. " | Time: "..math.floor(gameTime)
if not playing then return end
-- TIMER
if timerRunning and not paused and not isResuming then
gameTime -= dt
if gameTime <= 0 then
gameTime = 0
timerRunning = false
paused = true
resultText.Text = "Score: "..score..
"\nNotes Hit: "..notesHit..
"\nNotes Spawned: "..notesSpawned
resultFrame.Visible = true
main.Visible = false
return
end
end
-- RESUME TIMER
if isResuming then
resumeTimer -= dt
if resumeTimer <= 0 then
isResuming = false
paused = false
pauseBtn.Text = "Pause"
end
return
end
-- SPAWN NOTES
if not paused then
spawnTimer += dt
if spawnTimer >= spawnDelay then
spawnTimer = 0
spawnDelay = math.random(MIN_DELAY * 100, MAX_DELAY * 100) / 100
spawnMultipleNotes()
end
for i = #notes,1,-1 do
local n = notes[i]
n.obj.Position += UDim2.fromOffset(0,NOTE_SPEED*dt)
if n.obj.AbsolutePosition.Y > hitLine.AbsolutePosition.Y + HIT_WINDOW then
n.obj:Destroy()
table.remove(notes,i)
combo = 0
misses += 1
end
end
end
end)



w