Start typing to search packages!
wire
By @elentium
Roblox
MirroredWire
A high-performance, signal and request library for Roblox. Wire provides lightweight alternatives to `BindableEvent` and `BindableFunction` with superior performance and a clean, functional API.
Features
- Blazing Fast — Outperforms GoodSignal, SignalPlus, and FastSignal in benchmarks
- O(1) Disconnect — Doubly-linked list architecture enables constant-time connection removal
- Thread Pool Recycling — Efficient coroutine reuse for async operations
- Parallel Luau Support — First-class support for parallel execution with
connectParallel - Dual Paradigm — Both event-style signals and RPC-style requests
- Type-Safe — Full Luau strict mode with exported types
- Zero Dependencies — Pure Luau implementation
- Memory-Efficient — Instead of creating a table & using metatables for every signal object, it simply creates an ID and only stores head & tail connections when needed
Installation
Wally
[dependencies]
Wire = "elentium/wire@0.0.4"
Manual
install the roblox-direct/Wire.rbxm and insert in studio
Quick Start
local Wire = require(path.to.Wire)
-- Create a signal
local PlayerDamaged = Wire.signal()
-- Connect a listener
local connection = Wire.connect(PlayerDamaged, function(player, damage)
print(player.Name .. " took " .. damage .. " damage!")
end)
-- Fire the signal
Wire.fire(PlayerDamaged, player, 25)
-- Disconnect when done
connection:disconnect()
API Reference
Constructors
Wire.signal() -> number
Creates a new signal and returns its entity ID.
local MySignal = Wire.signal()
Wire.request(callback?) -> number
Creates a new request (similar to BindableFunction) and returns its entity ID.
local GetPlayerData = Wire.request(function(player)
return playerDataStore[player]
end)
Signal Methods
Wire.connect(entityID, callback) -> WireConnection
Connects a callback to a signal. Returns a connection object.
local connection = Wire.connect(MySignal, function(...)
print("Signal fired with:", ...)
end)
Wire.connectParallel(entityID, callback) -> WireConnection
Connects a callback that runs in parallel (for Parallel Luau).
Wire.connectParallel(HeavyComputation, function(data)
-- This runs desynchronized from the main thread
processData(data)
end)
Wire.once(entityID, callback) -> WireConnection
Connects a callback that automatically disconnects after the first fire.
Wire.once(GameStarted, function()
print("Game has started!")
end)
Wire.onceParallel(entityID, callback) -> WireConnection
Combines once and connectParallel — runs once in parallel, then disconnects.
Wire.fire(entityID, ...) -> ()
Fires a signal synchronously. All callbacks execute sequentially in the current thread.
Wire.fire(MySignal, "arg1", "arg2", 123)
Recommended for non-yielding callbacks. Most performant option.
Wire.fireSafe(entityID, ...) -> ()
Fires a signal synchronously. All callbacks are wrapped in pcall and are executed sequentially in the current thread.
Wire.fire(MySignal, "arg1", "arg2", 123)
Recommended for non-yielding callbacks that can error and you do not want it to stop other connections.
Wire.fireAsync(entityID, ...) -> ()
Fires a signal asynchronously. Each callback runs in its own coroutine.
Wire.fireAsync(MySignal, data)
Use when callbacks may yield (e.g., contain
task.wait, HTTP requests, etc.)
Wire.await(entityID) -> ...any
Yields the current thread until the signal fires, then returns the fired arguments.
local damage, attacker = Wire.await(PlayerDamaged)
print("Received damage:", damage, "from", attacker)
Wire.disconnectAll(entityID) -> ()
Disconnects all connections from a signal. Can also serve as a signal destructor.
Wire.disconnectAll(MySignal)
Request Methods
Wire.onInvoke(entityID, callback) -> ()
Sets or updates the callback for a request.
Wire.onInvoke(GetPlayerData, function(player)
return database:GetAsync(player.UserId)
end)
Wire.invoke(entityID, ...) -> ...any
Invokes a request and returns the result.
local data = Wire.invoke(GetPlayerData, player)
Wire.destroyRequest(entityID) -> ()
Removes the request callback, freeing the reference.
Wire.destroyRequest(GetPlayerData)
Connection Object
connection:disconnect() -> ()
Disconnects the connection from its signal.
local connection = Wire.connect(MySignal, callback)
-- Later...
connection:disconnect()
Performance
Benchmarks run with 10 connections per signal:
| Operation | Wire | GoodSignal | SignalPlus | FastSignal |
|---|---|---|---|---|
| Fire (100k iterations) | 0.490s | 0.552s | 0.587s | 0.677s |
| Disconnect (1k items) | 0.00007s | 0.0155s | 0.00008s | 0.00009s |
Wire achieves the fastest fire times and the fastest disconnect times thanks to its doubly-linked list architecture (O(1) removal vs O(n) for array-based implementations).
Best Practices
Use fire over fireAsync when possible
fire is significantly faster because it avoids coroutine overhead. Only use fireAsync when your callbacks yield.
-- ✅ Good: Non-yielding callback with fire
Wire.connect(DamageDealt, function(amount)
healthBar:Update(amount)
end)
Wire.fire(DamageDealt, 50)
-- ✅ Good: Yielding callback with fireAsync
Wire.connect(SaveData, function(player)
dataStore:SetAsync(player.UserId, getData(player))
end)
Wire.fireAsync(SaveData, player)
Handle errors in callbacks when using fire
With fire, an error in any callback will halt execution. Wrap risky code in pcall:
Wire.connect(RiskySignal, function(data)
local success, err = pcall(function()
processUnsafeData(data)
end)
if not success then
warn("Handler error:", err)
end
end)
Clean up connections
Always disconnect connections when they're no longer needed to prevent memory leaks:
local connections = {}
function module:Init()
table.insert(connections, Wire.connect(Signal1, handler1))
table.insert(connections, Wire.connect(Signal2, handler2))
end
function module:Destroy()
for _, conn in connections do
conn:disconnect()
end
table.clear(connections)
end
License
Apache-2.0 — See LICENSE for details.
Links
- GitHub: https://github.com/Elentium/Wire
- Wally:
elentium/wire@0.0.4
Package Details
Install command (Click to copy)
Version
0.0.4
License
Apache-2.0
Safe for commercial use
Modified files must carry a notice of changes. If the package ships a NOTICE file, its attributions must be preserved.
Automated license review — not legal advice.
