Binder - Intelligent Connection Management

Binder - Intelligent Connection Management System…


Introduction

Binder is a strictly-typed, metatable heavy connection management lib for Luau. It is designed to be easy to use, and familiar with the default connection system.


Why You Need this

  • Memory leaks:

    RBXScriptConnection and Signals are an extremely integral part of game development. e.g., PlayerAdded, PlayerRemoving, etc. If you forget to disconnect any connections - which is easy to forget, you add up a small performance hit.
    Lots of developers handle this with arrays, with repeated Disconnect calls scattered everywhere. This gets overwhelming quite fast; especially with larger projects.

  • Binder solves this for you:

    Wrap any SignalLike once, and every single connection made through it gets tracked automatically and intelligently, with minimal effort.

  • Built and tested:

    Binder was built from the ground up, and thoroughly unit-tested using TestEZ by Roblox.


How it works

Binder uses metatables and proxies to intercept calls and an intelligent and optimized ConnectionPoller. The main advantage of this is it requires little to no effort. You wrap a SignalLike once, and that signal is now tracked.

ConnectionPoller uses an optimized system to constantly check connections (incase of an external disconnection) over multiple frames. It also includes a cleanup interval (def: 60s) that cleans up all connections that may have made it through.

When no connections need cleanup, ConnectionPoller detects this and disconnects from RunService.


Performance Characteristics

  • Connection overhead: Near-zero. Proxy creation uses cached metatable detection and single table lookups.

  • Cleanup Detection: RBXScriptConnections are polled every few frames with optimized single-pass array compaction with the ConnectionPoller. Table-based connections like GoodSignal use instant metatable-based detection. Unfortunately, there’s no way currently to track when RBXScriptConnections get disconnected externally instantly - as you cannot attach a metatable to a RBXScriptConnection.

  • Memory Efficiency: Swapping array elements during removal maintains O(1) access patterns. No memory allocation during cleanup operations

  • Scaling behavior: Linear with active connection count, not total connections ever created. Disconnected connections are removed from tracking immediately


API

Basic Usage

Here’s a basic setup of Binder

local Binder = require(path.to.Binder)
local Signal = require(path.to.Signal)
local binder = Binder.new()
local mySignal = Signal.new()

-- Here's how we can register connections into binder...
local con = binder:bind(workspace.ChildAdded):Connect(function(child: Instance)
	print(child)
end)
-- Now 'con' is contained!
-- Even if it is disconnected externally, that change gets reflected due to the intelligent behavior of Binder

-- Also supports custom Signals...
binder:bind(mySignal):Connect(function(...)
    print(...)
end)
-- Note, ConnectParallel will error with GoodSignal. (Not supported- but included in the type)

-- We later fire the signal:
task.delay(5, function()
   mySignal:Fire("string", 5)
end)

-- We have options for cleanup...
binder:unbind(con) -- Cleans up the specific connection, in this case 'con'

-- This unbinds everything...
binder:unbindAll() -- Everything

-- A complete teardown, unbinds everything, cleans up the object. Cannot be used
binder:destroy() -- Complete teardown

-- binder:bind() -- This will error!

:warning: Ensure you are using the correct connection method provided by your signal implementation.

It is super easy to register connections as you can see above, and it supports custom signals like GoodSignal!
We can also create groups for unit control:

local Binder = require(game.ReplicatedStorage.SharedModules.Libraries.Binder)
local Signal = require(game.ReplicatedStorage.SharedModules.Libraries.Signal)

local binder = Binder.new()
local mySignal = Signal.new()

-- We can use `binder:bindGroup` to construct a group...
local group = binder:bindGroup({
	-- Construct ConnectionLike's returned from `bind` here...
	binder:bind(workspace.ChildAdded):Connect(function(child)
		print(child)
	end),
	-- Add a custom signal...
	binder:bind(mySignal):Connect(function(...)
		print(...)
	end)
})
-- We can add ConnectionLike's
local addedIndex = group:add(binder:bind(workspace.ChildRemoved):Connect(function(child)
	print(child)
end))
-- Removing ConnectionLike's
local removedCon = group:remove(1)

-- Options for cleanup...
-- Individual control:
group[addedIndex]:Disconnect() -- Disconnects the addedIndex connection from earlier
group[2]:Disconnect() -- Disconnects mySignal connection!
-- Mass control
group:disconnect() -- Disconnects EVERYTHING in the group! (Aside from removedCon, as it was removed!)
-- Full cleanup...
group:destroy()

:light_bulb: GroupHandle objects provide dual access patterns: use getConnections() for the full array, or index directly with group[1], group[2], etc. to access individual ConnectionLike objects.

Binder

Constructor

  • Binder.new() -> Binder: Creates a new Binder object for managing signal connections.

Methods

  • bind: <T...>(self: Binder, signal: SignalLike<T...>) -> SignalLike<T...>: Returns a proxy around the signal that tracks all connections made through it.

  • bindGroup: (self: Binder, connections: {ConnectionLike}) -> GroupHandle: Creates a GroupHandle object for batch management of multiple connections.

  • unbind: (self: Binder, connection: ConnectionLike) -> (): Disconnects and removes a specific connection from tracking. Most efficient cleanup method for ConnectionLike objects.

    :light_bulb:This is the optimal way of disconnecting ConnectionLike objects!

  • unbindGroup: (self: Binder, group: GroupHandle) -> (): Disconnects all connections in a GroupHandle. This is equivalent to group:disconnect.

  • unbindAll: (self: Binder) -> (): Disconnects every tracked connection in the Binder instance.

  • destroy: (self: Binder) -> (): Complete cleanup - disconnects everything and marks instance as destroyed.

    :warning: After calling destroy, the Binder object should not be used anymore as it will no longer function properly.

GroupHandle

Returned by binder:bindGroup(). Manages collections of connections

:light_bulb:GroupHandle is not directly exposed from Binder

Methods

  • add: (self: GroupHandle, connection: ConnectionLike) -> number: Adds a connection to the group, returns insertion index.

  • remove: (self: GroupHandle, index: number) -> ConnectionLike: Removes connection from group without disconnecting it. Returns the removed connection.

  • disconnect: (self: GroupHandle) -> (): Disconnects all connections in the group and clears the array.

    :light_bulb: The GroupHandle object is still usable after calling this method. If you have no use of the object, call destroy to free up memory

  • getConnections: (self: GroupHandle) -> {ConnectionLike}: Returns internal connection array.

    :warning: Returned array is mutable - treat as read-only

  • destroy: (self: GroupHandle) -> (): Disconnects all connections and destroys the GroupHandle object.

    :warning: After calling destroy, the GroupHandle object should not be used anymore as it will no longer function properly.

Signal Compatibility

  • RBXScriptSignals: Connect, ConnectParallel, Once
  • GoodSignal: Connect, Once
  • Custom Signals: Any signal with compatible methods/properties

:warning: Binder is thoroughly tested with both RBXScriptSignals and GoodSignal implementations. It is not guaranteed to work with other custom signal implementations other than GoodSignal. Other custom implementations could work though.


Warnings

  • Timing

    RBXScriptConnections have a small delay (~0.33ms) before removal from internal tracking when disconnected externally. Table-based connections are removed instantly. This doesn’t affect functionality, but may appear in debugging scenarios. This is intentional as to preserve the low performance overhead of Binder.
  • GroupHandle Arrays Should be Treated as Readonly

    getConnections() returns the actual internal array, not a copy. Modifying it directly bypasses Binder’s tracking and will cause issues. Treat the array as read-only.
  • Destroyed Objects Should not be Used

    Attempting to use Binder or GroupHandle methods after calling destroy() will assert and throw. This is intentional - destroyed objects should not be reused.
  • External Disconnections are Supported

    External disconnection is supported, but suboptimal. You can call Disconnect() directly on connections, but using binder:unbind() is faster, and more performance friendly for cleanup.

Changelog

V1.0.1 (Current)

  • Added support for Rojo via the Github releases page

License

Binder is under the MIT license. Feel free to use and modify for any Roblox projects.


Installation

Github page for Rojo users: GitHub - chumnyman/binder

You can download here on Roblox Creator Store: Binder - Intelligent Connection Management

You can also get the rbxm file:
binder.rbxm (13.6 KB)

Last Updated: 11/17/25
Author - RobloxJrTrainer
Current Version: 1.0.1


If you encounter bugs, let me know!
Made with :heart: ~ RobloxJrTrainer

5 Likes

Can you please do more research before saying something like that? It’s not AI, and I put a lot of hard-work into this.

Go ask AI to write a script, and you’ll find out.

Can you give evidence on why you think it’s AI at least? You are spreading information without providing any evidence.

1 Like

i don’t think it’s AI at all and I think this is a cool and somewhat useful module.

it would be nice if you had a function like getconnections to get all binder connections of a certain event or being able to retrieve the original function as i find myself writing functions with duplicate but i’m sure that wouldn’t be as useful to somebody but i know why i would want it

1 Like

Thanks!
I’m working on adding a bunch of debug tools within the Binder. So you’ll most likely see this when it gets updated!

2 Likes

Changelog v1.0.1:

  • Added support for Rojo users w/ a github
  • Removed unused function
  • Refactored the post to be more concise and use less in-your-face emojis

I stopped working on this for quite a bit as it wasn’t my top priority, but I’m hoping to release more frequent updates.

1 Like

This module seems very good for beginners cuz beginners often make mistakes and cause memory leaks, really good module :slightly_smiling_face:

1 Like