diff --git a/src/main.rs b/src/main.rs
index 337ba79..765d81c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,6 @@
 mod file;
 mod setup;
+mod timer;
 mod window;
 mod worker;
 mod physics;
diff --git a/src/timer.rs b/src/timer.rs
new file mode 100644
index 0000000..9f2570c
--- /dev/null
+++ b/src/timer.rs
@@ -0,0 +1,179 @@
+use strafesnet_common::integer::{Time,Ratio64};
+
+pub trait TimerState:Copy{
+	fn get_time(&self,time:Time)->Time;
+	fn set_time(&mut self,time:Time,new_time:Time);
+	fn get_offset(&self)->Time;
+	fn set_offset(&mut self,offset:Time);
+}
+
+#[derive(Clone,Copy,Debug)]
+struct Scaled{
+	scale:Ratio64,
+	offset:Time,
+}
+impl Scaled{
+	fn scale(&self,time:Time)->Time{
+		Time::raw(self.scale.mul_int(time.get()))
+	}
+	fn get_scale(&self)->Ratio64{
+		self.scale
+	}
+	fn set_scale(&mut self,time:Time,new_scale:Ratio64){
+		let new_time=self.get_time(time);
+		self.scale=new_scale;
+		self.set_time(time,new_time);
+	}
+}
+impl TimerState for Scaled{
+	fn get_time(&self,time:Time)->Time{
+		self.scale(time)+self.offset
+	}
+	fn set_time(&mut self,time:Time,new_time:Time){
+		self.offset=new_time-self.scale(time);
+	}
+	fn get_offset(&self)->Time{
+		self.offset
+	}
+	fn set_offset(&mut self,offset:Time){
+		self.offset=offset;
+	}
+}
+
+#[derive(Clone,Copy,Debug)]
+struct Realtime{
+	offset:Time,
+}
+impl TimerState for Realtime{
+	fn get_time(&self,time:Time)->Time{
+		time+self.offset
+	}
+	fn set_time(&mut self,time:Time,new_time:Time){
+		self.offset=new_time-time;
+	}
+	fn get_offset(&self)->Time{
+		self.offset
+	}
+	fn set_offset(&mut self,offset:Time){
+		self.offset=offset;
+	}
+}
+
+#[derive(Clone,Debug)]
+pub struct Timer<T>{
+	state:T,
+	paused:bool,
+}
+
+impl Timer<Realtime>{
+	pub fn realtime(offset:Time)->Self{
+		Self{
+			state:Realtime{offset},
+			paused:false,
+		}
+	}
+	pub fn realtime_paused(offset:Time)->Self{
+		Self{
+			state:Realtime{offset},
+			paused:true,
+		}
+	}
+}
+
+#[derive(Debug)]
+pub enum Error{
+	AlreadyPaused,
+	AlreadyUnpaused,
+}
+impl std::fmt::Display for Error{
+	fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
+		write!(f,"{self:?}")
+	}
+}
+impl std::error::Error for Error{}
+
+impl Timer<Scaled>{
+	pub fn scaled(scale:Ratio64,offset:Time)->Self{
+		Self{
+			state:Scaled{scale,offset},
+			paused:false,
+		}
+	}
+	pub fn scaled_paused(scale:Ratio64,offset:Time)->Self{
+		Self{
+			state:Scaled{scale,offset},
+			paused:true,
+		}
+	}
+	pub fn get_scale(&mut self)->Ratio64{
+		self.state.get_scale()
+	}
+	pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){
+		self.state.set_scale(time,new_scale)
+	}
+}
+
+impl<T:TimerState> Timer<T>{
+	pub fn time(&self,time:Time)->Time{
+		match self.paused{
+			true=>self.state.get_offset(),
+			false=>self.state.get_time(time),
+		}
+	}
+	pub fn set_time(&mut self,time:Time,new_time:Time){
+		match self.paused{
+			true=>self.state.set_offset(new_time),
+			false=>self.state.set_time(time,new_time),
+		}
+	}
+	pub fn pause(&mut self,time:Time)->Result<(),Error>{
+		match self.paused{
+			true=>Err(Error::AlreadyPaused),
+			false=>{
+				let new_time=self.time(time);
+				self.state.set_offset(new_time);
+				self.paused=true;
+				Ok(())
+			},
+		}
+	}
+	pub fn unpause(&mut self,time:Time)->Result<(),Error>{
+		match self.paused{
+			true=>{
+				let new_time=self.time(time);
+				self.state.set_time(time,new_time);
+				self.paused=false;
+				Ok(())
+			},
+			false=>Err(Error::AlreadyUnpaused),
+		}
+	}
+}
+#[cfg(test)]
+mod test{
+	use super::{Time,Timer,Error};
+	macro_rules! sec {
+		($s: expr) => {
+			Time::from_secs($s)
+		};
+	}
+	#[test]
+	fn test_timer()->Result<(),Error>{
+		//create a paused timer that reads 0s
+		let mut timer=Timer::realtime_paused(sec!(0));
+		//the paused timer at 1 second should read 0s
+		assert_eq!(timer.time(sec!(1)),sec!(0));
+
+		//unpause it after one second
+		timer.unpause(sec!(1))?;
+		//the timer at 6 seconds should read 5s
+		assert_eq!(timer.time(sec!(6)),sec!(5));
+
+		//pause the timer after 11 seconds
+		timer.pause(sec!(11))?;
+		//the paused timer at 20 seconds should read 10s
+		assert_eq!(timer.time(sec!(20)),sec!(10));
+
+		Ok(())
+	}
+}