diff --git a/src/lib.rs b/src/lib.rs
index ddfa819825c97c7e7058069615a104d3e06368d5..d989e4f81c07e7da3b55d0c3e8117325bbbf424a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,10 +1,12 @@
 #![warn(clippy::all, rust_2018_idioms)]
 
+mod transfer_functions;
+
 #[allow(unused_imports)]
 use basic_print::basic_print; // basic print for print-debugging
 
-use pole_position::PolePos;
-use frequency_response::FreqResp;
+use frequency_response_app::FreqResp;
+use pole_position_app::PolePos;
 
 pub struct ControlApp {
     cur_app_idx: Option<usize>,
@@ -12,8 +14,32 @@ pub struct ControlApp {
 }
 
 trait CentralApp {
-    fn draw_app(&mut self, ui: &mut egui::Ui);
     fn get_label(&self) -> &str;
+    fn draw_app(&mut self, ui: &mut egui::Ui);
+}
+
+impl eframe::App for ControlApp {
+    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
+        egui::TopBottomPanel::top("app_selection_panel").show(ctx, |ui| {
+            if self.top_bar(ui) {
+                #[cfg(not(target_arch = "wasm32"))] // no quit on web pages!
+                _frame.close();
+            }
+        });
+
+        egui::CentralPanel::default().show(ctx, |ui| {
+            ui.centered_and_justified(|ui| {
+                match self.cur_app_idx {
+                    None => {
+                        ui.label("Select an application in the bar above.");
+                    }
+                    Some(idx) => {
+                        self.apps[idx].draw_app(ui);
+                    }
+                };
+            });
+        });
+    }
 }
 
 impl ControlApp {
@@ -22,15 +48,23 @@ impl ControlApp {
         // `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
 
         let apps: Vec<Box<dyn CentralApp>> = vec![
-            Box::new(PolePos::new("Pole Positioning".to_string() )),
-            Box::new(FreqResp::new("Frequency Response".to_string() )),
+            Box::new(PolePos::new("Pole Positioning".to_string())),
+            Box::new(FreqResp::new("Frequency Response".to_string())),
         ];
 
-        // ControlApp{cur_app_idx: None, apps }
-        ControlApp{cur_app_idx: Some(0), apps }
+        if cfg!(debug_assertions) {
+            ControlApp {
+                cur_app_idx: Some(0),
+                apps,
+            }
+        } else {
+            ControlApp {
+                cur_app_idx: None,
+                apps,
+            }
+        }
     }
 
-
     fn top_bar(&mut self, ui: &mut egui::Ui) -> bool {
         #[allow(unused_mut)]
         let mut quit = false;
@@ -63,68 +97,37 @@ impl ControlApp {
                 }
                 egui::warn_if_debug_build(ui);
             });
-
-        });
-
-        return quit
-    }
-
-}
-
-impl eframe::App for ControlApp {
-
-    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
-        egui::TopBottomPanel::top("app_selection_panel").show(ctx, |ui| {
-            if self.top_bar(ui) {
-                #[cfg(not(target_arch = "wasm32"))] // no quit on web pages!
-                _frame.close();
-            }
         });
 
-        egui::CentralPanel::default().show(ctx, |ui| {
-            ui.centered_and_justified(|ui| {
-                match self.cur_app_idx {
-                    None => {
-                        ui.label("Select an application in the bar above.");
-                    },
-                    Some(idx) => {self.apps[idx].draw_app(ui);},
-                };
-
-            });
-        });
+        return quit;
     }
 }
 
-
-mod pole_position {
+mod pole_position_app {
     #[allow(unused_imports)]
     use basic_print::basic_print; // basic print for print-debugging
 
-    use egui::plot::{ Line, LineStyle, Plot, PlotPoint, PlotPoints, PlotUi, Points, MarkerShape, };
-    use egui::{ Ui, Color32, Vec2, Layout, Align, InnerResponse, Response, };
+    use egui::{Ui, Vec2};
 
-    use crate::LinearSystems::*;
+    use crate::transfer_functions::*;
     use crate::CentralApp;
 
-    use std::f64::consts::PI;
-    use std::ops::Range;
-    use std::rc::Rc;
+    use super::tf_plots;
 
-
-    #[derive(PartialEq,Debug,Clone,Copy)]
+    #[derive(PartialEq, Debug, Clone, Copy)]
     enum Order {
         First,
         Second,
     }
 
-    #[derive(PartialEq,Debug,Clone,Copy)]
+    #[derive(PartialEq, Debug, Clone, Copy)]
     enum Display {
         StepResponse,
         BodeDiagram,
     }
 
     #[derive(Debug)]
-    pub struct PolePos{
+    pub struct PolePos {
         label: String,
 
         order: Order,
@@ -133,72 +136,49 @@ mod pole_position {
         fo: FirstOrderSystem,
         so: SecondOrderSystem,
 
-        pole_drag_offset: Option<(f64,f64)>,
+        pole_drag_offset: Option<(f64, f64)>,
     }
 
-
-
     impl PolePos {
         pub fn new(label: String) -> PolePos {
-            PolePos{
+            PolePos {
                 label,
                 order: Order::First,
                 display: Display::StepResponse,
-                fo: FirstOrderSystem{T: 1.0},
-                so: SecondOrderSystem{d: 0.5, w: 0.75},
+                fo: FirstOrderSystem { T: 1.0 },
+                so: SecondOrderSystem { d: 0.5, w: 0.75 },
                 pole_drag_offset: None,
             }
         }
 
-
         fn pole_plot(&mut self, ui: &mut Ui, width: f32, height: f32) {
-            // Plot params
-            let cross_radius = 10.0;
-            let re_bounds = -3.0..1.0;
-            let im_bounds = -1.0..1.0;
-
-
-            // Plot data
-            let p = match self.order {
-                Order::First => {self.fo.poles()},
-                Order::Second => {self.so.poles()},
+            let (dragged, pointer_coordinate) = match self.order {
+                Order::First =>
+                    tf_plots::pole_plot(&self.fo, ui, width, height),
+                Order::Second =>
+                    tf_plots::pole_plot(&self.so, ui, width, height),
             };
 
-            let data = Points::new(p);
-            let unit_circle = Line::new(PlotPoints::from_parametric_callback(|t| (t.sin(), t.cos()), 0.0..(2.0*PI), 100));
-
-            // Plot
-            let resp = plot_show(
-                ui, "Pole Placement",
-                width, height, re_bounds, im_bounds,
-                |plot| {plot.data_aspect(1.0)},
-                |plot_ui| {
-                    plot_ui.line( unit_circle.color(Color32::GRAY) );
-                    plot_ui.points( data.shape(MarkerShape::Cross).color(Color32::BLACK).radius(cross_radius) );
-                    plot_ui.pointer_coordinate()
-                });
-            let InnerResponse{ response: _, inner: (show_response, pointer) } = resp;
-
             // Handle dragging
-            if show_response.dragged() {
+            if dragged {
                 let (re_off, im_off) = match self.pole_drag_offset {
-                    Some((x,y)) => (x,y),
+                    Some((x, y)) => (x, y),
                     None => {
                         // We have just started to drag, find offset to closest pole so we don't jump
                         //
                         // For now, just assume we want to place the pole where we click
                         self.pole_drag_offset = Some((0.0, 0.0));
-                        (0.0,0.0)
-                    },
+                        (0.0, 0.0)
+                    }
                 };
 
-
-                if let Some(PlotPoint{x:re,y:im}) = pointer { // This should never fail
-                    let (re, im) = (re+re_off, im+im_off);
+                if let Some((re,im)) = pointer_coordinate {
+                    // This should never fail
+                    let (re, im) = (re + re_off, im + im_off);
 
                     match self.order {
-                        Order::First => self.fo.adjust_poles_to(re,im),
-                        Order::Second => self.so.adjust_poles_to(re,im),
+                        Order::First => self.fo.adjust_poles_to(re, im),
+                        Order::Second => self.so.adjust_poles_to(re, im),
                     };
                 }
             } else {
@@ -207,71 +187,21 @@ mod pole_position {
         }
 
         fn step_response_plot(&mut self, ui: &mut Ui, width: f32, height: f32) {
-            // Plot params
-            let n_samples = 100;
-            let t_end = 10.0;
-            let pad_ratio = 0.1;
-
-            // Calculate plot bounds
-            let t_bounds = (0.0 - t_end*pad_ratio)..(t_end + t_end*pad_ratio);
-            let y_bounds = (0.0-pad_ratio)..(1.0+pad_ratio);
-
-            // Plot data
-            let sys: Box<dyn LinearSystem> = match self.order {
-                Order::First => Box::new(self.fo),
-                Order::Second => Box::new(self.so),
+            let (_dragged, _pointer_coordinate) = match self.order {
+                Order::First =>
+                    tf_plots::step_response_plot(&self.fo, ui, width, height),
+                Order::Second =>
+                    tf_plots::step_response_plot(&self.so, ui, width, height),
             };
-            let data = Line::new(PlotPoints::from_explicit_callback(move |t| sys.step_response(t), t_bounds.clone(), n_samples));
-
-            // Plot
-            plot_show(
-                ui, "Step Response",
-                width, height, t_bounds, y_bounds,
-                |plot| {plot},
-                |plot_ui| {
-                    plot_ui.line( data.color(Color32::RED).style(LineStyle::Solid) );
-                });
         }
 
         fn bode_plot(&mut self, ui: &mut Ui, width: f32, height: f32) {
-            // Plot params
-            let n_samples = 100;
-            let w_bounds_exp = -4.0..2.0;
-
-            // Plot data
-            let sys: Rc<dyn LinearSystem> = match self.order {
-                Order::First => Rc::new(self.fo),
-                Order::Second => Rc::new(self.so),
+            let (_amp_dragged, _amp_pointer, _ph_dragged, _ph_pointer) = match self.order {
+                Order::First => tf_plots::bode_plot(&self.fo, ui, width, height),
+                Order::Second => tf_plots::bode_plot(&self.so, ui, width, height),
             };
-            let sys_cb = sys.clone();
-            let amp_data = Line::new(PlotPoints::from_explicit_callback(move |we| sys_cb.bode_amplitude(10f64.powf(we)).log10(), w_bounds_exp.clone(), n_samples));
-
-            let sys_cb = sys.clone();
-            let phase_data = Line::new(PlotPoints::from_explicit_callback(move |we| sys_cb.bode_phase(10f64.powf(we)), w_bounds_exp.clone(), n_samples));
-
-            // Plot
-            ui.allocate_ui_with_layout(
-                Vec2{x: width, y: height},
-                Layout::top_down(Align::LEFT),
-                |ui| {
-                    let height = (height - ui.spacing().item_spacing.y)/2.0;
-                    plot_show(
-                        ui, "Bode Plote - Amplitude", width, height, w_bounds_exp.clone(), -4.0..5f64.log10(),
-                        |plot| {plot},
-                        |plot_ui| {
-                            plot_ui.line( amp_data.color(Color32::RED).style(LineStyle::Solid) );
-                        });
-                    plot_show(
-                        ui, "Bode Plote - Phase", width, height, w_bounds_exp.clone(), -PI/2.0..PI/2.0,
-                        |plot| {plot},
-                        |plot_ui| {
-                            plot_ui.line( phase_data.color(Color32::RED).style(LineStyle::Solid) );
-                        });
-                });
         }
 
-
-
         fn order_selection(&mut self, ui: &mut Ui) {
             ui.heading("Select System Order");
             ui.horizontal(|ui| {
@@ -292,117 +222,130 @@ mod pole_position {
             match self.order {
                 Order::First => {
                     ui.heading("G(s) = 1/(sT - 1)");
-                    ui.add(egui::Slider::new(&mut self.fo.T, 0.2..=100.0)
-                           .text("T")
-                           .logarithmic(true)
-                          );
-                },
+                    ui.add(
+                        egui::Slider::new(&mut self.fo.T, 0.2..=100.0)
+                            .text("T")
+                            .logarithmic(true),
+                    );
+                }
                 Order::Second => {
                     ui.heading("G(s) = ω^2/(s^2 + 2δωs+ ω^2)");
                     ui.add(egui::Slider::new(&mut self.so.d, 0.0..=1.5).text("δ"));
                     ui.add(egui::Slider::new(&mut self.so.w, 0.0..=2.0).text("ω"));
-                },
+                }
             };
         }
     }
 
-
     impl CentralApp for PolePos {
         fn draw_app(&mut self, ui: &mut Ui) {
             // ui.spacing_mut().item_spacing.x = 10.0;
 
             let max_width = 550.0;
-            let Vec2{x,y} = ui.available_size();
+            let Vec2 { x, y } = ui.available_size();
             let is_vertical = x < max_width;
 
-
             if is_vertical {
-
-                egui::Grid::new("app_grid")
-                    .num_columns(1)
-                    .show(ui, |ui| {
-
-                        ui.vertical(|ui| {
-                            self.order_selection(ui);
-                            ui.separator();
-                            self.display_selection(ui);
-                            ui.separator();
-                            self.parameter_sliders(ui);
-                            ui.separator();
-                        });
-                        ui.end_row();
-
-                        let Vec2{x,y} = ui.available_size();
-                        let mut width = x;
-                        let mut height = y/2.0;
-
-                        if width >= height*1.75 {
-                            width = height*1.75
-                        } else {
-                            height = width/1.75
-                        }
-
-                        self.pole_plot(ui, width, height);
-                        ui.end_row();
-
-                        match self.display {
-                            Display::StepResponse => self.step_response_plot(ui, width, height),
-                            Display::BodeDiagram => self.bode_plot(ui, width, height),
-                        };
+                egui::Grid::new("app_grid").num_columns(1).show(ui, |ui| {
+                    ui.vertical(|ui| {
+                        self.order_selection(ui);
+                        ui.separator();
+                        self.display_selection(ui);
+                        ui.separator();
+                        self.parameter_sliders(ui);
+                        ui.separator();
                     });
+                    ui.end_row();
 
-            } else {
-                let mut width = (x/2.0).min(max_width);
-                let mut height = y/2.0;
+                    let Vec2 { x, y } = ui.available_size();
+                    let mut width = x;
+                    let mut height = y / 2.0;
 
-                if width >= height*1.75 {
-                    width = height*1.75
-                } else {
-                    height = width/1.75
-                }
+                    if width >= height * 1.75 {
+                        width = height * 1.75
+                    } else {
+                        height = width / 1.75
+                    }
 
-                egui::Grid::new("app_grid")
-                    .num_columns(2)
-                    .show(ui, |ui| {
+                    self.pole_plot(ui, width, height);
+                    ui.end_row();
 
-                        ui.vertical(|ui| {
-                            self.order_selection(ui);
-                            ui.add_space(20.0);
-                            self.parameter_sliders(ui);
-                        });
+                    match self.display {
+                        Display::StepResponse => self.step_response_plot(ui, width, height),
+                        Display::BodeDiagram => self.bode_plot(ui, width, height),
+                    };
+                });
+            } else {
+                let mut width = (x / 2.0).min(max_width);
+                let mut height = y / 2.0;
 
-                        self.pole_plot(ui, width, height);
+                if width >= height * 1.75 {
+                    width = height * 1.75
+                } else {
+                    height = width / 1.75
+                }
 
-                        ui.end_row();
-                        self.step_response_plot(ui, width, height);
-                        self.bode_plot(ui, width, height);
+                egui::Grid::new("app_grid").num_columns(2).show(ui, |ui| {
+                    ui.vertical(|ui| {
+                        self.order_selection(ui);
+                        ui.add_space(20.0);
+                        self.parameter_sliders(ui);
                     });
-            }
 
+                    self.pole_plot(ui, width, height);
 
+                    ui.end_row();
+                    self.step_response_plot(ui, width, height);
+                    self.bode_plot(ui, width, height);
+                });
+            }
         }
 
         fn get_label(&self) -> &str {
             &self.label
         }
     }
+}
+
+
 
 
-    fn plot_show<R>(
-        ui: &mut Ui, title: &str,
-        width: f32, height: f32, x_bounds: Range<f64>, y_bounds: Range<f64>,
+mod tf_plots {
+    use egui::plot::{ Line, LineStyle, MarkerShape, Plot, PlotPoints, PlotUi, Points, };
+    use egui::{ Align, Color32, InnerResponse, Layout, Ui, Vec2, };
+
+    use std::f64::consts::PI;
+    use std::ops::Range;
+
+    use crate::transfer_functions::*;
+
+    // Helper that give a sane default plot window. Looks can be modified with the second to last
+    // argument and what is plotted is given by the last. Returns whether the plot is dragged by
+    // the mouse and the plot coordinate of the mouse.
+    fn plot_show(
+        ui: &mut Ui,
+        title: &str,
+        width: f32,
+        height: f32,
+        x_bounds: Range<f64>,
+        y_bounds: Range<f64>,
         plot_mod_fn: impl FnOnce(Plot) -> Plot,
-        build_fn: impl FnOnce(&mut PlotUi) -> R,
-        ) -> InnerResponse<(Response,R)>
+        build_fn: impl FnOnce(&mut PlotUi),
+    ) -> (bool, Option<(f64, f64)>)
     {
-        ui.allocate_ui_with_layout(
-            Vec2{x: width, y: height},
+        let InnerResponse {
+            response: _,
+            inner: (dragged, pointer_coordinate),
+        } = ui.allocate_ui_with_layout(
+            Vec2 {
+                x: width,
+                y: height,
+            },
             Layout::top_down(Align::LEFT),
             |ui| {
                 ui.heading(title);
 
-                let mut plot =
-                    Plot::new(title)
+                let mut plot = Plot::new(title)
                     .allow_scroll(false)
                     .allow_zoom(false)
                     .allow_boxed_zoom(false)
@@ -413,143 +356,170 @@ mod pole_position {
                     .include_x(x_bounds.end)
                     .include_y(y_bounds.start)
                     .include_y(y_bounds.end)
-                    .set_margin_fraction(Vec2{x:0.0, y:0.0})
-                    ;
+                    .set_margin_fraction(Vec2 { x: 0.0, y: 0.0 });
 
                 plot = plot_mod_fn(plot);
 
-                let InnerResponse{response: show_response, inner: build_resp} = plot.show(ui, build_fn );
-                (show_response, build_resp)
-            })
-    }
-
-}
-
-
-
-
+                let InnerResponse {
+                    response: show_response,
+                    inner: pointer_coordinate,
+                } = plot.show(ui, |plot_ui| {
+                    build_fn(plot_ui);
+                    plot_ui.pointer_coordinate().map(|pp| (pp.x, pp.y))
+                });
 
-pub mod LinearSystems {
-    #![allow(non_snake_case)]
+                (show_response.dragged(), pointer_coordinate)
+            },
+        );
 
-    pub trait LinearSystem {
-        fn step_response(&self, t: f64) -> f64;
-        fn bode_amplitude(&self, w: f64) -> f64;
-        fn bode_phase(&self, w: f64) -> f64;
-        fn poles(&self) -> Vec<[f64; 2]>;
-        fn adjust_poles_to(&mut self, re: f64, im: f64);
+        (dragged, pointer_coordinate)
     }
 
-    #[derive(Debug,Clone,Copy)]
-    pub struct FirstOrderSystem {
-        // first order system 1/(sT + 1)
-        // pole = -1/T
-        // https://www.tutorialspoint.com/control_systems/control_systems_response_first_order.htm
-         pub T: f64,
+    pub fn pole_plot(
+        tf: &impl TransferFunction,
+        ui: &mut Ui,
+        width: f32,
+        height: f32,
+    ) -> (bool, Option<(f64, f64)>)
+    {
+        // Plot params
+        let cross_radius = 10.0;
+        let re_bounds = -3.0..1.0;
+        let im_bounds = -1.0..1.0;
+
+        // Plot data
+        let data = Points::new(tf.poles());
+        let unit_circle = Line::new(PlotPoints::from_parametric_callback(
+            |t| (t.sin(), t.cos()),
+            0.0..(2.0 * PI),
+            100,
+        ));
+
+        // Plot
+        plot_show(
+            ui,
+            "Pole Placement",
+            width,
+            height,
+            re_bounds,
+            im_bounds,
+            |plot| plot.data_aspect(1.0),
+            |plot_ui| {
+                plot_ui.line(unit_circle.color(Color32::GRAY));
+                plot_ui.points(
+                    data.shape(MarkerShape::Cross)
+                        .color(Color32::BLACK)
+                        .radius(cross_radius),
+                );
+            },
+        )
     }
 
-    impl LinearSystem for FirstOrderSystem {
-        fn poles(&self) -> Vec<[f64; 2]> {
-            vec![[-1.0/self.T, 0.0]]
-        }
-
-        fn step_response(&self, t: f64) -> f64 {
-            if t >= 0.0 {
-                1.0 - (-t/self.T).exp()
-            } else {
-                0.0
-            }
-        }
-
-        fn bode_amplitude(&self, w: f64) -> f64 {
-            1.0 / ( ((w*self.T).powi(2) + 1.0).sqrt() )
-        }
-
-        fn bode_phase(&self, w: f64) -> f64 {
-            -(w*self.T).atan()
-        }
-
-        fn adjust_poles_to(&mut self, re: f64, _im: f64) {
-            let pole_bound = -0.01;
-
-
-            if re >= pole_bound {
-                self.T = -1.0/pole_bound;
-            } else {
-                self.T = -1.0/re;
-            }
+    pub fn step_response_plot(
+        tf: &impl TransferFunction,
+        ui: &mut Ui,
+        width: f32,
+        height: f32,
+    ) -> (bool, Option<(f64, f64)>)
+    {
+        // Plot params
+        let n_samples = 100;
+        let t_end = 10.0;
+        let pad_ratio = 0.1;
+
+        // Calculate plot bounds
+        let t_bounds = (0.0 - t_end * pad_ratio)..(t_end + t_end * pad_ratio);
+        let y_bounds = (0.0 - pad_ratio)..(1.0 + pad_ratio);
+
+        // Calc plot data
+        let step = (t_bounds.end - t_bounds.start) / ((n_samples - 1) as f64);
+        let mut points: Vec<[f64; 2]> = Vec::new();
+        for i in 0..n_samples {
+            let t = t_bounds.start + step * (i as f64);
+            points.push([t, tf.step_response(t)]);
         }
+        let data = Line::new(points);
+
+        // Plot
+        plot_show(
+            ui,
+            "Step Response",
+            width,
+            height,
+            t_bounds,
+            y_bounds,
+            |plot| plot,
+            |plot_ui| {
+                plot_ui.line(data.color(Color32::RED).style(LineStyle::Solid));
+            },
+        )
     }
 
-    #[derive(Debug,Clone,Copy)]
-    pub struct SecondOrderSystem { // TODO fix this, the step response is not corerct
-        // second order system w^2/(s^2 + 2dw s + w^2)
-        // poles = -dw +- w sqrt(d^2 - 1)
-        // https://www.tutorialspoint.com/control_systems/control_systems_response_second_order.htm
-        pub d: f64,
-        pub w: f64,
-    }
-
-    impl LinearSystem for SecondOrderSystem {
-        fn poles(&self) -> Vec<[f64; 2]> {
-            let (d,w) = (self.d, self.w);
-
-            if d == 0.0 {
-                vec![
-                    [0.0, w],
-                    [0.0, -w],
-                ]
-            } else if (0.0 < d) && (d < 1.0) {
-                vec![
-                    [-d*w, (1.0-d.powi(2)).sqrt()*w],
-                    [-d*w, -(1.0-d.powi(2)).sqrt()*w],
-                ]
-            } else if d == 1.0 {
-                vec![
-                    [-w, 0.0],
-                    [-w, 0.0],
-                ]
-            } else {
-                vec![
-                    [-d*w + (d.powi(2) -1.0).sqrt()*w, 0.0],
-                    [-d*w - (d.powi(2) -1.0).sqrt()*w, 0.0],
-                ]
-            }
-        }
-
-        fn step_response(&self, t: f64) -> f64 {
-            let (d,w) = (self.d, self.w);
-
-            if t < 0.0 {
-                return 0.0
-            }
-
-            if d == 0.0 {
-                1.0 - (w*t).cos()
-            } else if (0.0 < d) && (d < 1.0) {
-                let d_1_sqrt = (1.0 - d.powi(2)).sqrt();
-                let th = d_1_sqrt.asin();
-                1.0 - ( (-w*t).exp() )*( (w*t + th).sin() )/ d_1_sqrt
-            } else if d == 1.0 {
-                1.0 - ( (-w*t).exp() )*(1.0 + w*t)
-            } else {
-                let d_1_sqrt = (d.powi(2)-1.0).sqrt();
-                1.0
-                    + (-t*w*(d + d_1_sqrt)).exp() / (2.0*(d+d_1_sqrt)*d_1_sqrt)
-                    - (-t*w*(d - d_1_sqrt)).exp() / (2.0*(d-d_1_sqrt)*d_1_sqrt)
-            }
-        }
-
-        fn bode_amplitude(&self, _w: f64) -> f64 {
-            1.0
-        }
-
-        fn bode_phase(&self, _w: f64) -> f64 {
-            0.0
+    pub fn bode_plot(
+        tf: &impl TransferFunction,
+        ui: &mut Ui,
+        width: f32,
+        height: f32,
+    ) -> (bool, Option<(f64, f64)>, bool, Option<(f64, f64)>)
+    {
+        // Plot params
+        let n_samples = 100;
+        let w_bounds_exp = -4.0..2.0;
+
+        // Calc plot data
+        let step = (w_bounds_exp.end - w_bounds_exp.start) / ((n_samples - 1) as f64);
+        let mut amp_points: Vec<[f64; 2]> = Vec::new();
+        let mut phase_points: Vec<[f64; 2]> = Vec::new();
+        for i in 0..n_samples {
+            let we = w_bounds_exp.start + step * (i as f64);
+            let w = 10f64.powf(we);
+            amp_points.push([we, tf.bode_amplitude(w).log10()]);
+            phase_points.push([we, tf.bode_phase(w)]);
         }
+        let amp_data = Line::new(amp_points);
+        let phase_data = Line::new(phase_points);
+
+        // Plot
+        let InnerResponse {
+            response: _,
+            inner: (amp_dragged, amp_pointer, ph_dragged, ph_pointer),
+        } = ui.allocate_ui_with_layout(
+            Vec2 {
+                x: width,
+                y: height,
+            },
+            Layout::top_down(Align::LEFT),
+            |ui| {
+                let height = (height - ui.spacing().item_spacing.y) / 2.0;
+                let (amp_dragged, amp_pointer) = plot_show(
+                    ui,
+                    "Bode Plote - Amplitude",
+                    width,
+                    height,
+                    w_bounds_exp.clone(),
+                    -4.0..5f64.log10(),
+                    |plot| plot,
+                    |plot_ui| {
+                        plot_ui.line(amp_data.color(Color32::RED).style(LineStyle::Solid));
+                    },
+                    );
+                let (ph_dragged, ph_pointer) = plot_show(
+                    ui,
+                    "Bode Plote - Phase",
+                    width,
+                    height,
+                    w_bounds_exp.clone(),
+                    -PI / 2.0..PI / 2.0,
+                    |plot| plot,
+                    |plot_ui| {
+                        plot_ui.line(phase_data.color(Color32::RED).style(LineStyle::Solid));
+                    },
+                    );
+                (amp_dragged, amp_pointer, ph_dragged, ph_pointer)
+            },
+            );
 
-        fn adjust_poles_to(&mut self, _re: f64, _im: f64) {
-        }
+        (amp_dragged, amp_pointer, ph_dragged, ph_pointer)
     }
 }
 
@@ -557,29 +527,16 @@ pub mod LinearSystems {
 
 
 
-
-
-
-
-
-
-
-
-
-
-
-mod frequency_response {
+mod frequency_response_app {
     use crate::CentralApp;
 
-    pub struct FreqResp{
+    pub struct FreqResp {
         label: String,
     }
 
     impl FreqResp {
         pub fn new(label: String) -> FreqResp {
-            FreqResp{
-                label,
-            }
+            FreqResp { label }
         }
     }
 
diff --git a/src/transfer_functions.rs b/src/transfer_functions.rs
new file mode 100644
index 0000000000000000000000000000000000000000..caaad8e1783f703bccdcc3688b0393f5e44680ce
--- /dev/null
+++ b/src/transfer_functions.rs
@@ -0,0 +1,115 @@
+#![allow(non_snake_case)]
+
+pub trait TransferFunction {
+    fn step_response(&self, t: f64) -> f64;
+    fn bode_amplitude(&self, w: f64) -> f64;
+    fn bode_phase(&self, w: f64) -> f64;
+    fn poles(&self) -> Vec<[f64; 2]>;
+    fn adjust_poles_to(&mut self, re: f64, im: f64);
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct FirstOrderSystem {
+    // first order system 1/(sT + 1)
+    // pole = -1/T
+    // https://www.tutorialspoint.com/control_systems/control_systems_response_first_order.htm
+    pub T: f64,
+}
+
+impl TransferFunction for FirstOrderSystem {
+    fn poles(&self) -> Vec<[f64; 2]> {
+        vec![[-1.0 / self.T, 0.0]]
+    }
+
+    fn step_response(&self, t: f64) -> f64 {
+        if t >= 0.0 {
+            1.0 - (-t / self.T).exp()
+        } else {
+            0.0
+        }
+    }
+
+    fn bode_amplitude(&self, w: f64) -> f64 {
+        1.0 / (((w * self.T).powi(2) + 1.0).sqrt())
+    }
+
+    fn bode_phase(&self, w: f64) -> f64 {
+        -(w * self.T).atan()
+    }
+
+    fn adjust_poles_to(&mut self, re: f64, _im: f64) {
+        let pole_bound = -0.01;
+
+        if re >= pole_bound {
+            self.T = -1.0 / pole_bound;
+        } else {
+            self.T = -1.0 / re;
+        }
+    }
+}
+
+
+
+#[derive(Debug, Clone, Copy)]
+pub struct SecondOrderSystem {
+    // TODO fix this, the step response is not corerct
+    // second order system w^2/(s^2 + 2dw s + w^2)
+    // poles = -dw +- w sqrt(d^2 - 1)
+    // https://www.tutorialspoint.com/control_systems/control_systems_response_second_order.htm
+    pub d: f64,
+    pub w: f64,
+}
+
+impl TransferFunction for SecondOrderSystem {
+    fn poles(&self) -> Vec<[f64; 2]> {
+        let (d, w) = (self.d, self.w);
+
+        if d == 0.0 {
+            vec![[0.0, w], [0.0, -w]]
+        } else if (0.0 < d) && (d < 1.0) {
+            vec![
+                [-d * w, (1.0 - d.powi(2)).sqrt() * w],
+                [-d * w, -(1.0 - d.powi(2)).sqrt() * w],
+            ]
+        } else if d == 1.0 {
+            vec![[-w, 0.0], [-w, 0.0]]
+        } else {
+            vec![
+                [-d * w + (d.powi(2) - 1.0).sqrt() * w, 0.0],
+                [-d * w - (d.powi(2) - 1.0).sqrt() * w, 0.0],
+            ]
+        }
+    }
+
+    fn step_response(&self, t: f64) -> f64 {
+        let (d, w) = (self.d, self.w);
+
+        if t < 0.0 {
+            return 0.0;
+        }
+
+        if d == 0.0 {
+            1.0 - (w * t).cos()
+        } else if (0.0 < d) && (d < 1.0) {
+            let d_1_sqrt = (1.0 - d.powi(2)).sqrt();
+            let th = d_1_sqrt.asin();
+            1.0 - ((-w * t).exp()) * ((w * t + th).sin()) / d_1_sqrt
+        } else if d == 1.0 {
+            1.0 - ((-w * t).exp()) * (1.0 + w * t)
+        } else {
+            let d_1_sqrt = (d.powi(2) - 1.0).sqrt();
+            1.0 + (-t * w * (d + d_1_sqrt)).exp() / (2.0 * (d + d_1_sqrt) * d_1_sqrt)
+                - (-t * w * (d - d_1_sqrt)).exp() / (2.0 * (d - d_1_sqrt) * d_1_sqrt)
+        }
+    }
+
+    fn bode_amplitude(&self, _w: f64) -> f64 {
+        1.0
+    }
+
+    fn bode_phase(&self, _w: f64) -> f64 {
+        0.0
+    }
+
+    fn adjust_poles_to(&mut self, _re: f64, _im: f64) {}
+}