atherhubather.hub
Back to Guides
SUNC
9 min read
May 11, 2026

SUNC Input Synthesis

SUNC provides a small family of functions for synthesising user input — pressing keys, clicking the mouse, moving the cursor — without the user actually doing it. They're how an external script can drive Roblox UI, automate repetitive actions, or build features that would otherwise require holding the mouse manually. This article covers each one with a working example.

Ather
Ather
Lead developer at Atherhub. Writes about Roblox internals, Luau, script engineering, and platform security.Last updated May 11, 2026

Why these are different from RBXScriptSignal:Fire

You can fire UserInputService.InputBegan manually with firesignal — but that only notifies listeners you can find. The real input pipeline is wider than that: there's ContextActionService, custom event hooks, and the Roblox engine's own built-in input handling for camera and movement. Real synthetic input — the kind that's indistinguishable from a user's — has to enter at the OS or low-level input-system level.

The SUNC input functions do exactly that: they call into the host's input API as if the user had pressed the key or moved the mouse. Every consumer of that input sees it as real.

keypress and keyrelease

luau
keypress(keycode: number): ()
keyrelease(keycode: number): ()
Returnsvoid
No return value. Synthesises a key event at the OS level.
-- Press W for one second
keypress(0x57)         -- VK_W on Windows
task.wait(1)
keyrelease(0x57)

The argument is a virtual key code from the host operating system, not a Enum.KeyCode. On Windows that's a Win32 VK constant (0x57 = W, 0x20 = Space, 0x0D = Enter). Most executors that implement SUNC accept these codes uniformly.

Practical pattern — auto-hold a movement key:

luau
local UserInputService = game:GetService("UserInputService")
local holdingW = false

UserInputService.InputBegan:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.F2 then
        if holdingW then
            keyrelease(0x57)
        else
            keypress(0x57)
        end
        holdingW = not holdingW
    end
end)

mouse1click, mouse2click

luau
mouse1click(): ()
mouse2click(): ()
Returnsvoid
A single click of the left (mouse1) or right (mouse2) mouse button. Synthesises both the press and release with default timing.
-- Single left-click at current cursor position
mouse1click()

These are shorthand for "press, release." For finer control you can use mouse1press, mouse1release, and the same pair for right mouse. Same applies for the scroll wheel: many SUNC implementations expose mousescroll as well.

mousemoverel and mousemoveabs

luau
mousemoverel(dx: number, dy: number): ()
mousemoveabs(x: number, y: number): ()
Returnsvoid
Moves the cursor either relatively (by dx, dy from current position) or absolutely (to x, y in screen coordinates).
-- Slowly drag from the current spot to 100px to the right
for i = 1, 100 do
    mousemoverel(1, 0)
    task.wait(0.01)
end

-- Or, jump straight to a specific coordinate
mousemoveabs(640, 360)  -- centre of a 1280x720 viewport

The relative form is what camera systems use — sending tiny deltas every frame to look around naturally. The absolute form is useful for clicking specific on-screen elements you've already located.

Coordinate system
mousemoveabs uses Roblox viewport coordinates (where (0, 0) is the top-left of the game window, not the OS desktop). If you need OS-level coordinates instead, some executors expose mousemoveabsdesktop; check your executor's docs.

A worked example: aim-assist style smoothing

A small but realistic example — when the user holds a key, smoothly nudge the cursor toward a target screen position. This is what real aim-assist features look like under the hood: a per-frame delta computation that shrinks each tick.

luau
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local active = false

UserInputService.InputBegan:Connect(function(i) if i.KeyCode == Enum.KeyCode.E then active = true end end)
UserInputService.InputEnded:Connect(function(i) if i.KeyCode == Enum.KeyCode.E then active = false end end)

RunService.RenderStepped:Connect(function(dt)
    if not active then return end

    local target = findTargetScreenPos()  -- your game-specific function
    if not target then return end

    local cursor = UserInputService:GetMouseLocation()
    local dx = (target.X - cursor.X) * 5 * dt
    local dy = (target.Y - cursor.Y) * 5 * dt

    if math.abs(dx) >= 1 or math.abs(dy) >= 1 then
        mousemoverel(dx, dy)
    end
end)

The math: every frame, compute the delta between cursor and target, scale by a small per-second factor timesdt, send the delta. The cursor approaches the target asymptotically without ever jumping — natural- looking from the outside.

Common key codes

A small reference table of the Windows virtual key codes you'll need most often:

luau
local VK = {
    A = 0x41, B = 0x42, C = 0x43, D = 0x44, E = 0x45, F = 0x46,
    G = 0x47, H = 0x48, I = 0x49, J = 0x4A, K = 0x4B, L = 0x4C,
    M = 0x4D, N = 0x4E, O = 0x4F, P = 0x50, Q = 0x51, R = 0x52,
    S = 0x53, T = 0x54, U = 0x55, V = 0x56, W = 0x57, X = 0x58,
    Y = 0x59, Z = 0x5A,

    Space = 0x20, Enter = 0x0D, Shift = 0x10, Ctrl = 0x11, Alt = 0x12,
    Tab = 0x09, Escape = 0x1B, Backspace = 0x08,

    Left = 0x25, Up = 0x26, Right = 0x27, Down = 0x28,

    F1 = 0x70, F2 = 0x71, F3 = 0x72, F4 = 0x73,
    F5 = 0x74, F6 = 0x75, F7 = 0x76, F8 = 0x77,
}

On macOS the underlying codes differ, but most executors translate the Windows codes for you so your scripts stay portable across platforms.

Caveats

  • Server validation still applies. Synthetic input fires through the normal Roblox pipeline, including the server. A game that validates actions against impossible speeds or rates will still catch you.
  • Focus matters. If the Roblox window isn't focused, the OS may drop the synthetic input. Real scripts confirm the window is in focus before firing.
  • Rate limits. Firing mousemoverel 1000 times in one frame can overwhelm the input queue. Throttle to one per frame (RenderStepped) for smooth movement.
  • User experience. Synthetic input the user didn't expect feels terrible. Always tie automation to a clear, user- controlled trigger — a held key, a toggle, an explicit start command.

Wrap-up

Input synthesis is one of the smallest SUNC libraries but probably the one with the most visible effect on the end user. Four core functions (keypress, keyrelease, mouse1click, mousemoverel) cover most use cases. The real engineering is what you wire them into — when to fire, how often, what triggers them. That part stays tasteful.

Ather
Written by Ather

Ather is the lead developer behind Atherhub. He's been writing Luau and Roblox tooling for the better part of a decade, with a focus on the messy interface between game-script internals and the platforms that host them. Have feedback on this article? Drop it in the Discord.