diff --git a/src/runner/mod.rs b/src/runner/mod.rs
index 7d716eb..a208868 100644
--- a/src/runner/mod.rs
+++ b/src/runner/mod.rs
@@ -7,7 +7,8 @@ mod color3;
 mod cframe;
 mod vector3;
 pub mod instance;
-mod number_sequence;
+mod script_signal;
 mod color_sequence;
+mod number_sequence;
 
 pub use runner::{Runner,Runnable,Error};
diff --git a/src/runner/script_signal.rs b/src/runner/script_signal.rs
new file mode 100644
index 0000000..f436046
--- /dev/null
+++ b/src/runner/script_signal.rs
@@ -0,0 +1,72 @@
+use std::{cell::RefCell,rc::Rc};
+
+#[derive(Clone)]
+pub struct ScriptSignal{
+	// Emulate the garbage roblox api.
+	// ScriptConnection should not exist.
+	// :Disconnect should be a method on ScriptSignal, and this would be avoided entirely.
+	callbacks:Rc<RefCell<Vec<mlua::Function>>>,
+}
+pub struct ScriptConnection{
+	signal:ScriptSignal,
+	function:mlua::Function,
+}
+impl ScriptSignal{
+	pub fn new()->Self{
+		Self{
+			callbacks:Rc::new(RefCell::new(Vec::new())),
+		}
+	}
+	// This eats the Lua error
+	pub fn fire(&self,args:mlua::MultiValue){
+		// Make a copy of the list in case Lua attempts to modify it during the loop
+		let functions=self.callbacks.borrow().clone();
+		for function in functions{
+			//wee let's allocate for our function calls
+			if let Err(e)=function.call::<mlua::MultiValue>(args.clone()){
+				println!("Script Signal Error: {e}");
+			}
+		}
+	}
+	pub fn connect(&self,function:mlua::Function)->ScriptConnection{
+		self.callbacks.borrow_mut().push(function.clone());
+		ScriptConnection{
+			signal:self.clone(),
+			function,
+		}
+	}
+}
+impl ScriptConnection{
+	pub fn position(&self)->Option<usize>{
+		self.signal.callbacks.borrow().iter().position(|function|function==&self.function)
+	}
+}
+
+impl mlua::UserData for ScriptSignal{
+	fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
+		methods.add_method("Connect",|_lua,this,f:mlua::Function|
+			Ok(this.connect(f))
+		);
+		// Fire is not allowed to be called from Lua
+		// methods.add_method("Fire",|_lua,this,args:mlua::MultiValue|
+		// 	Ok(this.fire(args))
+		// );
+	}
+}
+//type_from_lua_userdata!(ScriptSignal);
+
+impl mlua::UserData for ScriptConnection{
+	fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
+		fields.add_field_method_get("Connected",|_,this|{
+			Ok(this.position().is_some())
+		});
+	}
+	fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
+		methods.add_method("Disconnect",|_,this,_:()|{
+			if let Some(index)=this.position(){
+				this.signal.callbacks.borrow_mut().remove(index);
+			}
+			Ok(())
+		});
+	}
+}