Browse
Script Types
Tools
Community
Reach players with keyless, universal, or place-specific scripts.


This Slap Ball script by DonnieAzoff is a simple open-source tool focused on one function: Perfect Slap. It handles the slap timing for you, which is useful when hitting the ball at the correct moment normally requires precise input.
There are no extra farming or movement tools — the script is made specifically for players who want more consistent slap timing. It is also no key, so there is no separate key-system step before execution.
Open Slap Ball and wait for the match to load.
Copy the DonnieAzoff script.
Open a compatible Roblox executor.
Paste the code and press Execute.
Play normally and let Perfect Slap handle the supported slap timing.
Because the script is open source, you can inspect the code before running it. Game updates may still change the slap system and break the function.
This script loads code from an external source. Review the URL and understand what it does before running it in Roblox.
https://raw.githubusercontent.com/deividcomsono/Obsidian/main/
1local Players = game:GetService("Players")2local ReplicatedStorage = game:GetService("ReplicatedStorage")3local RunService = game:GetService("RunService")4local CoreGui = game:GetService("CoreGui")5local LocalPlayer = Players.LocalPlayer67local oldGui = CoreGui:FindFirstChild("SlapBallGui")8if oldGui then9 pcall(function()10 oldGui:Destroy()11 end)12end1314local Tetherball = ReplicatedStorage:WaitForChild("Tetherball")15local Config = require(Tetherball:WaitForChild("Config"))16local BallMath = require(Tetherball:WaitForChild("BallMath"))17local Remotes = Tetherball:WaitForChild("Remotes")18local ClientInput = Remotes:WaitForChild("ClientInput")19local MatchEvent = Remotes:WaitForChild("MatchEvent")20local Courts = workspace:WaitForChild("TetherballCourts")2122local TAU = 6.28318530717958623local PI = 3.14159265358979324local MAX_REWIND = Config.MaxRewind or 0.3525local REWIND_SAFE = MAX_REWIND * 0.7826local PERFECT_ANGLE = Config.PerfectAngle or 0.1047197551196597827local LOCKOUT = Config.MissClickLockout or 0.752829local court, side, sideAngle30local serveArmed = false31local serveRetry = 032local lastStamp = 033local lockUntil = 034local pending = nil35local scheduled = nil36local gen = 037local clockOffset = 038local latency = 0.0639local samples = {}40local freeKey, freeList41local matchHitOk = false42local badStreak = 043local failStreak = 044local marginIndex = 145local margins = {46 { value = 0.06, ok = 0, fail = 0 },47 { value = -0.03, ok = 0, fail = 0 },48 { value = 0.15, ok = 0, fail = 0 },49 { value = 0, ok = 0, fail = 0 },50}51local connections = {}52local stateConn5354local function track(conn)55 table.insert(connections, conn)56 return conn57end5859local function angDiff(a, b)60 return math.atan2(math.sin(a - b), math.cos(a - b))61end6263local function serverNow()64 return workspace:GetServerTimeNow() + clockOffset65end6667local function readState()68 if not court or not court.Parent then69 return nil70 end71 return BallMath.decode(court:GetAttribute(BallMath.ATTR))72end7374local function stateKey(state)75 if not state then76 return "nil"77 end78 return string.format("%s|%.5f|%.5f|%.5f", tostring(state.m), state.t or 0, state.o or 0, state.th or 0)79end8081local function noteLatency(state)82 if not state or type(state.t) ~= "number" then83 return84 end85 local delta = workspace:GetServerTimeNow() - state.t86 if delta ~= delta or math.abs(delta) > 5 then87 return88 end89 table.insert(samples, delta)90 if #samples > 24 then91 table.remove(samples, 1)92 end93 local lowest = math.huge94 for _, value in samples do95 if value < lowest then96 lowest = value97 end98 end99 if lowest < 0 then100 clockOffset = clockOffset - lowest + 0.002101 table.clear(samples)102 return103 end104 latency = math.clamp(lowest, 0.01, 0.4)105end106107local function resetMatch()108 pending = nil109 scheduled = nil110 lastStamp = 0111 lockUntil = 0112 freeKey = nil113 freeList = nil114 gen = gen + 1115end116117local function bindCourt(model, index)118 court = model119 side = index120 sideAngle = index == 1 and 0 or PI121 resetMatch()122 if stateConn then123 stateConn:Disconnect()124 stateConn = nil125 end126 if court then127 stateConn = court:GetAttributeChangedSignal(BallMath.ATTR):Connect(function()128 noteLatency(readState())129 scheduled = nil130 end)131 track(stateConn)132 end133end134135local function flipSide()136 if not side then137 return138 end139 bindCourt(court, side == 1 and 2 or 1)140end141142local function resolveCourt()143 local character = LocalPlayer.Character144 local root = character and character:FindFirstChild("HumanoidRootPart")145 if not root then146 return147 end148 local bestModel, bestIndex, bestDist149 for _, model in Courts:GetChildren() do150 if model:IsA("Model") then151 for index = 1, 2 do152 local pad = model:FindFirstChild("PlayerPad" .. index)153 if pad then154 local dist = (pad.Position - root.Position).Magnitude155 if dist <= 18 and (not bestDist or dist < bestDist) then156 bestModel, bestIndex, bestDist = model, index, dist157 end158 end159 end160 end161 end162 if bestModel and (bestModel ~= court or bestIndex ~= side) then163 bindCourt(bestModel, bestIndex)164 end165end166167local function rallyCandidates(state, from, out)168 local o = state.o169 if not o or o == 0 then170 return171 end172 local theta = BallMath.eval(state, from)173 local dir = o >= 0 and 1 or -1174 local travel = (angDiff(sideAngle, theta) * dir) % TAU175 local period = 1 / math.abs(o)176 local first = from + (travel / TAU) * period177 for index = 0, 3 do178 table.insert(out, first + period * index)179 end180end181182local function freeCandidates(state, from, out)183 local key = stateKey(state)184 if key ~= freeKey or not freeList then185 local found = {}186 local step = 1 / 240187 local limit = from + 6188 local prevT = from189 local prev = angDiff(BallMath.eval(state, prevT), sideAngle)190 local t = from191 while t < limit and #found < 4 do192 t = t + step193 local cur = angDiff(BallMath.eval(state, t), sideAngle)194 if (prev >= 0) ~= (cur >= 0) and math.abs(prev) + math.abs(cur) < 0.9 then195 local lo, hi = prevT, t196 for _ = 1, 30 do197 local mid = (lo + hi) * 0.5198 if (angDiff(BallMath.eval(state, mid), sideAngle) >= 0) == (prev >= 0) then199 lo = mid200 else201 hi = mid202 end203 end204 table.insert(found, (lo + hi) * 0.5)205 end206 prev, prevT = cur, t207 end208 freeKey = key209 freeList = found210 end211 for _, value in freeList do212 table.insert(out, value)213 end214end215216local function currentMargin()217 if failStreak >= 2 then218 local bestIndex, bestScore219 for index, entry in margins do220 if index ~= marginIndex then221 local score = (entry.ok + 1) / (entry.ok + entry.fail + 2)222 if not bestScore or score > bestScore then223 bestIndex, bestScore = index, score224 end225 end226 end227 if bestIndex then228 marginIndex = bestIndex229 end230 failStreak = 0231 end232 return margins[marginIndex]233end234235local function sendClick(stamp, marginUsed)236 lastStamp = stamp237 pending = {238 stamp = stamp,239 margin = marginUsed,240 clock = os.clock(),241 tries = 1,242 }243 ClientInput:FireServer("click", stamp)244end245246local function scoreOutcome(perfect)247 local entry = pending and pending.margin or margins[marginIndex]248 if perfect then249 entry.ok = entry.ok + 1250 failStreak = 0251 else252 entry.fail = entry.fail + 1253 failStreak = failStreak + 1254 end255end256257track(MatchEvent.OnClientEvent:Connect(function(name, data)258 data = data or {}259 if name == "MatchStart" or name == "Queued" then260 if data.court and data.side then261 bindCourt(data.court, data.side)262 end263 serveArmed = false264 matchHitOk = false265 badStreak = 0266 elseif name == "ServeReady" then267 serveArmed = data.server == LocalPlayer268 serveRetry = 0269 resetMatch()270 elseif name == "Turn" then271 serveArmed = false272 elseif name == "HitResult" then273 if side and data.side == side then274 matchHitOk = true275 badStreak = 0276 local rtt = pending and (os.clock() - pending.clock) or nil277 if rtt and rtt > 0 and rtt < 1.5 then278 latency = math.clamp(math.min(latency, rtt * 0.5), 0.01, 0.4)279 end280 scoreOutcome(data.grade == "Perfect")281 pending = nil282 end283 elseif name == "BadClick" then284 scoreOutcome(false)285 pending = nil286 scheduled = nil287 lockUntil = serverNow() + LOCKOUT + 0.05288 if not matchHitOk then289 badStreak = badStreak + 1290 if badStreak >= 2 then291 badStreak = 0292 flipSide()293 end294 end295 end296end))297298local repo = "https://raw.githubusercontent.com/deividcomsono/Obsidian/main/"299local Library = loadstring(game:HttpGet(repo .. "Library.lua"))()300local SaveManager = loadstring(game:HttpGet(repo .. "addons/SaveManager.lua"))()301local ThemeManager = loadstring(game:HttpGet(repo .. "addons/ThemeManager.lua"))()302303Library.ForceCheckbox = false304Library.ShowToggleFrameInKeybinds = true305306local Window = Library:CreateWindow({307 Title = "Slap Ball",308 Footer = "Slap Ball | Made by DonnieAzoff",309 AutoShow = true,310 NotifySide = "Right",311 ShowCustomCursor = false,312})313314local MainTab = Window:AddTab("Main", "swords")315local SlapGroup = MainTab:AddLeftGroupbox("Combat", "zap")316317SlapGroup:AddToggle("PerfectSlap", {318 Text = "PerfectSlap",319 Default = false,320})321322local CreditsGroup = MainTab:AddRightGroupbox("Credits", "badge-check")323CreditsGroup:AddLabel("Slap Ball")324CreditsGroup:AddLabel("Script made by DonnieAzoff")325326local SettingsTab = Window:AddTab("Settings", "sliders-horizontal")327328local candidates = {}329330local function enabled()331 local toggle = Library.Toggles.PerfectSlap332 return toggle and toggle.Value333end334335local function commit(cross, marginUsed, key)336 if not enabled() then337 return338 end339 local state = readState()340 if not state or stateKey(state) ~= key then341 return342 end343 local now = serverNow()344 local stamp = cross345 if now - stamp > REWIND_SAFE then346 stamp = now - REWIND_SAFE347 end348 if math.abs(angDiff(BallMath.eval(state, stamp), sideAngle)) > PERFECT_ANGLE * 0.4 then349 return350 end351 sendClick(stamp, marginUsed)352end353354local function step()355 if not enabled() then356 return357 end358 if not court or not court.Parent or not sideAngle then359 resolveCourt()360 if not court or not sideAngle then361 return362 end363 end364 local state = readState()365 if not state then366 return367 end368 local now = serverNow()369 if state.m == "rest" or state.m == "hold" then370 pending = nil371 scheduled = nil372 if serveArmed and now >= serveRetry and now >= lockUntil then373 serveRetry = now + 0.55374 ClientInput:FireServer("click", now)375 end376 return377 end378 if pending then379 local waited = os.clock() - pending.clock380 if waited > latency * 2 + 0.12 then381 if pending.tries < 3 and (now - pending.stamp) + latency < REWIND_SAFE then382 pending.tries = pending.tries + 1383 pending.clock = os.clock()384 ClientInput:FireServer("click", pending.stamp)385 else386 pending = nil387 end388 end389 end390 if now < lockUntil then391 return392 end393 local _, _, speed = BallMath.eval(state, now)394 if not speed or speed <= 0 then395 return396 end397 table.clear(candidates)398 local from = now - REWIND_SAFE * 0.9399 if state.m == "rally" then400 rallyCandidates(state, from, candidates)401 else402 freeCandidates(state, from, candidates)403 end404 if #candidates == 0 then405 return406 end407 table.sort(candidates)408 local target409 for _, cross in candidates do410 if cross > lastStamp + 0.12 then411 if cross >= now or (now - cross) + latency <= REWIND_SAFE then412 target = cross413 break414 end415 end416 end417 if not target then418 return419 end420 if scheduled and math.abs(scheduled - target) < 0.02 then421 return422 end423 local margin = currentMargin()424 local lead = math.clamp(latency - margin.value, 0, REWIND_SAFE * 0.85)425 local fireAt = target - lead426 local key = stateKey(state)427 local delay = fireAt - now428 if delay <= 0 then429 scheduled = target430 commit(target, margin, key)431 return432 end433 if delay <= 0.35 then434 scheduled = target435 local token = gen436 task.delay(delay, function()437 if token == gen then438 commit(target, margin, key)439 end440 end)441 end442end443444local function safeStep()445 local ok, err = pcall(step)446 if not ok then447 warn(err)448 end449end450451track(RunService.RenderStepped:Connect(safeStep))452track(RunService.Heartbeat:Connect(safeStep))453454SaveManager:SetLibrary(Library)455SaveManager:IgnoreThemeSettings()456SaveManager:SetFolder("Slap Ball")457SaveManager:SetIgnoreIndexes({ "MenuKeybind" })458459ThemeManager:SetLibrary(Library)460ThemeManager:SetFolder("Slap Ball")461462local ConfigGroupbox = SettingsTab:AddLeftGroupbox("Configuration", "folder-cog")463464ConfigGroupbox:AddInput("SaveManager_ConfigName", {465 Text = "Config name",466 Placeholder = "My Config",467})468469ConfigGroupbox:AddButton("Create config", function()470 local name = Library.Options.SaveManager_ConfigName.Value471 if name:gsub(" ", "") == "" then472 Library:Notify("Invalid config name (empty)", 2)473 return474 end475 local success, err = SaveManager:Save(name)476 if not success then477 Library:Notify("Failed to create config: " .. err)478 return479 end480 Library:Notify(string.format("Created config %q", name))481 Library.Options.SaveManager_ConfigList:SetValues(SaveManager:RefreshConfigList())482 Library.Options.SaveManager_ConfigList:SetValue(nil)483end)484485ConfigGroupbox:AddDivider()486487ConfigGroupbox:AddDropdown("SaveManager_ConfigList", {488 Text = "Config list",489 Values = SaveManager:RefreshConfigList(),490 AllowNull = true,491})492493ConfigGroupbox:AddButton("Load config", function()494 local name = Library.Options.SaveManager_ConfigList.Value495 local success, err = SaveManager:Load(name)496 if not success then497 Library:Notify("Failed to load config: " .. err)498 return499 end500 Library:Notify(string.format("Loaded config %q", name))501end)502503ConfigGroupbox:AddButton("Overwrite config", function()504 local name = Library.Options.SaveManager_ConfigList.Value505 local success, err = SaveManager:Save(name)506 if not success then507 Library:Notify("Failed to overwrite config: " .. err)508 return509 end510 Library:Notify(string.format("Overwrote config %q", name))511end)512513ConfigGroupbox:AddButton("Delete config", function()514 local name = Library.Options.SaveManager_ConfigList.Value515 local success, err = SaveManager:Delete(name)516 if not success then517 Library:Notify("Failed to delete config: " .. err)518 return519 end520 Library:Notify(string.format("Deleted config %q", name))521 Library.Options.SaveManager_ConfigList:SetValues(SaveManager:RefreshConfigList())522 Library.Options.SaveManager_ConfigList:SetValue(nil)523end)524525ConfigGroupbox:AddButton("Refresh list", function()526 Library.Options.SaveManager_ConfigList:SetValues(SaveManager:RefreshConfigList())527 Library.Options.SaveManager_ConfigList:SetValue(nil)528end)529530local AutoloadLabel = ConfigGroupbox:AddLabel("Current autoload config: " .. SaveManager:GetAutoloadConfig(), true)531532ConfigGroupbox:AddButton("Set as autoload", function()533 local name = Library.Options.SaveManager_ConfigList.Value534 local success, err = SaveManager:SaveAutoloadConfig(name)535 if not success then536 Library:Notify("Failed to set autoload config: " .. err)537 return538 end539 Library:Notify(string.format("Set %q to auto load", name))540 AutoloadLabel:SetText("Current autoload config: " .. name)541end)542543ConfigGroupbox:AddButton("Reset autoload", function()544 local success, err = SaveManager:DeleteAutoLoadConfig()545 if not success then546 Library:Notify("Failed to reset autoload config: " .. err)547 return548 end549 Library:Notify("Set autoload to none")550 AutoloadLabel:SetText("Current autoload config: none")551end)552553local MenuGroup = SettingsTab:AddRightGroupbox("Menu", "wrench")554555MenuGroup:AddLabel("Menu bind"):AddKeyPicker("MenuKeybind", {556 Default = "RightControl",557 NoUI = true,558 Text = "Menu keybind",559})560561MenuGroup:AddButton("Unload", function()562 Library:Unload()563end)564565SaveManager:SetIgnoreIndexes({ "SaveManager_ConfigList", "SaveManager_ConfigName", "MenuKeybind" })566567Library.ToggleKeybind = Library.Options.MenuKeybind568ThemeManager:ApplyToTab(SettingsTab)569570SaveManager:LoadAutoloadConfig()571572Library:OnUnload(function()573 gen = gen + 1574 for _, conn in connections do575 pcall(function()576 conn:Disconnect()577 end)578 end579 table.clear(connections)580 table.clear(samples)581 stateConn = nil582 court = nil583 side = nil584 sideAngle = nil585 serveArmed = false586 pending = nil587 scheduled = nil588 freeKey = nil589 freeList = nil590 print("Slap Ball by DonnieAzoff unloaded.")591end)
Comments · …
Loading comments…