diff --git a/src/Log.cpp b/src/Log.cpp
index 2b2181dabbe92d995793090240eb507b8ce4e89a..fe335622bd571b35b1d37360977139c5b0c85f80 100644
--- a/src/Log.cpp
+++ b/src/Log.cpp
@@ -24,7 +24,7 @@ namespace {
             loggerCache.insert(newEntry);
         }
         return loggerCache[lt]();
-}
+    }
 }
 
 Logger::Logger() :
diff --git a/src/URL.cpp b/src/URL.cpp
index 0efc3bcc9fc3279a985376777c8fd9bd7d29c914..6e6776ec324eaca4dfb2097a64003e2801045722 100644
--- a/src/URL.cpp
+++ b/src/URL.cpp
@@ -102,7 +102,7 @@ std::tuple<std::unique_ptr<URL>,enum URIParseError> parseUri(std::string raw) {
                 last = cursor + 1;
                 state = AUTHORITY;
                 if (uri->scheme == "file") {
-                    std::cout << "file scheme, current path[" << uri->path << "]" << std::endl;
+                    //std::cout << "URL:::parseUri file scheme, current path[" << uri->path << "]" << std::endl;
                     state = PATH;
                 }
             } else {
diff --git a/src/graphics/components/BoxComponent.cpp b/src/graphics/components/BoxComponent.cpp
index 72d764e18ec5e03d7315f68d00ed44ab90b829eb..5373131450d16e28ae9c4b5e0606ca2ad35c180f 100644
--- a/src/graphics/components/BoxComponent.cpp
+++ b/src/graphics/components/BoxComponent.cpp
@@ -151,6 +151,26 @@ void BoxComponent::render() {
     // can actuall delete vertices here
 }
 
+void BoxComponent::changeColor(const unsigned int hexColor) {
+    // really a texture swap
+    
+    // set texture color
+    data[0][0][0] = (hexColor >> 24) & 0xFF;
+    data[0][0][1] = (hexColor >> 16) & 0xFF;
+    data[0][0][2] = (hexColor >>  8) & 0xFF;
+    data[0][0][3] = (hexColor >>  0) & 0xFF;
+    
+    glBindVertexArray(vertexArrayObject);
+    
+    // now replace existing texture
+    glBindTexture(GL_TEXTURE_2D, texture);
+    
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+    glGenerateMipmap(GL_TEXTURE_2D);
+    
+    glBindVertexArray(0); // unbind to prevent anything from accidentally changing it
+}
+
 // this seems to work 17-08-10
 // for box
 // anime girl, we'll get aspect ratio breakage
@@ -158,7 +178,7 @@ void BoxComponent::resize(const int passedWindowWidth, const int passedWindowHei
     //std::cout << "BoxComponent::resize" << std::endl;
     windowWidth = passedWindowWidth;
     windowHeight = passedWindowHeight;
-
+    
     //std::cout << "BoxComponent::resize - boundToPage " << boundToPage << std::endl;
     // figure out new vertices
     float vx = x;
@@ -185,77 +205,3 @@ void BoxComponent::resize(const int passedWindowWidth, const int passedWindowHei
     
     verticesDirty = true;
 }
-
-void BoxComponent::changeColor(const unsigned int hexColor) {
-    // really a texture swap
-    
-    // set texture color
-    data[0][0][0] = (hexColor >> 24) & 0xFF;
-    data[0][0][1] = (hexColor >> 16) & 0xFF;
-    data[0][0][2] = (hexColor >>  8) & 0xFF;
-    data[0][0][3] = (hexColor >>  0) & 0xFF;
-    
-    glBindVertexArray(vertexArrayObject);
-    
-    // now replace existing texture
-    glBindTexture(GL_TEXTURE_2D, texture);
-    
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
-    glGenerateMipmap(GL_TEXTURE_2D);
-    
-    glBindVertexArray(0); // unbind to prevent anything from accidentally changing it
-}
-
-/*
-void BoxComponent::resize(const int passedWindowWidth, const int passedWindowHeight) {
-
-    std::cout << "BoxComponent::resize" << std::endl;
-    windowWidth = passedWindowWidth;
-    windowHeight = passedWindowHeight;
-    
-    if (boundToPage) {
-        std::cout << "BoxComponent::resize - boundToPage doing nothing" << std::endl;
-    } else {
-        
-        // figure out our percentages
-        // 0 / WW = 0
-        // 768 / 1024 = 75%
-        float xPer = initialX / (float)initialWindowWidth;
-        float yPer = initialY / (float)initialWindowHeight;
-        // adjust width
-        float wPer = initialWidth / (float)initialWindowWidth;
-        //float hPer = initialHeight / (float)initialWindowHeight;
-        
-        float vx = xPer * windowWidth;
-        float vy = yPer * windowHeight;
-        
-        // nah I don't think we want things to stretch
-        //float vWidth = wPer * windowWidth;
-        //float vHeight = hPer * windowHeight;
-        
-        // ugh these just need to be relaid out
-        //float vWidth = width;
-        float vWidth = wPer * windowWidth;
-        float vHeight = height;
-        
-        //std::cout << "initial: " << initialX << "x" << initialY << " size: " << initialWidth << "x" << initialHeight << " window size: " << initialWindowWidth << "x" << initialWindowHeight << std::endl;
-
-        //std::cout << "scaled to: " << (int)vx << "x" << (int)vy << " size: " << (int)vWidth << "x" << (int)vHeight << " window size: " << windowWidth << "x" << windowHeight << std::endl;
-
-        
-        pointToViewport(vx, vy);
-        distanceToViewport(vWidth, vHeight);
-        
-        vertices[(0 * 5) + 0] = vx;
-        vertices[(0 * 5) + 1] = vy + vHeight;
-        vertices[(1 * 5) + 0] = vx + vWidth;
-        vertices[(1 * 5) + 1] = vy + vHeight;
-        vertices[(2 * 5) + 0] = vx + vWidth;
-        vertices[(2 * 5) + 1] = vy;
-        vertices[(3 * 5) + 0] = vx;
-        vertices[(3 * 5) + 1] = vy;
-        
-        verticesDirty = true;
-    }
-}
-*/
diff --git a/src/graphics/components/Component.cpp b/src/graphics/components/Component.cpp
index 27700f4d78326d2461f1b60c9c11fc522902dd91..e9d57f917ad546960a372e7f61bee17bc39feb25 100644
--- a/src/graphics/components/Component.cpp
+++ b/src/graphics/components/Component.cpp
@@ -107,7 +107,7 @@ void Component::layout() {
         std::cout << "Component::layout[" << textComponent->text << "]" << std::endl;
     }
     */
-    //std::cout << "Component::layout - name: " << name << " boundToPage: " << boundToPage << std::endl;
+    //std::cout << "Component::layout - name: " << name << " type " << typeOfComponent(this) << " boundToPage: " << boundToPage << std::endl;
     
     // if we're a child, get our parents position
     if (parent && boundToPage) {
@@ -216,6 +216,7 @@ void Component::layout() {
 // resize/wordwrap to available width
 // our position is required
 void Component::wrap() {
+    //std::cout << "Component::wrap - " << typeOfComponent(this) << std::endl;
     float lW = width;
     float lH = height;
     resize(windowWidth, windowHeight); // this may change our w/h
@@ -232,6 +233,7 @@ void Component::wrap() {
 // measure current, apply changes to parent
 // our size is required
 void Component::updateParentSize() {
+    //std::cout << "Component::updateParentSize - " << typeOfComponent(this) << std::endl;
     if (!parent) {
         //std::cout << "Component::updateParentSize - can't update parent size, no parent" << std::endl;
         return;
@@ -246,7 +248,6 @@ void Component::updateParentSize() {
     // find max width of all siblings
     unsigned int maxWidth = width; // float?
     unsigned int heightAccum = 0;
-    bool wasInline =  false;
     unsigned int totalHeight = 0;
 
     // integrity check
@@ -257,6 +258,7 @@ void Component::updateParentSize() {
     }
     
     // look at siblings
+    bool wasInline =  false;
     for (std::shared_ptr<Component> child : parent->children) {
         maxWidth = std::max(maxWidth, static_cast<unsigned int>(child->width));
         if (child->isInline) {
@@ -421,7 +423,7 @@ void Component::printComponentTree(const std::shared_ptr<Component> &component,
     }
     InputComponent *inputComponent = dynamic_cast<InputComponent*>(component.get());
     if (inputComponent) {
-        std::cout << std::fixed << "X: " << static_cast<int>(inputComponent->x) << " Y: " << static_cast<int>(inputComponent->y) << " WIDTH: " << static_cast<int>(inputComponent->width) << " HEIGHT: " << static_cast<int>(inputComponent->height) << " INLINE: " << inputComponent->isInline << " INPUT: " << inputComponent->value << std::endl;
+        std::cout << std::fixed << "X: " << static_cast<int>(inputComponent->x) << " Y: " << static_cast<int>(inputComponent->y) << " WIDTH: " << static_cast<int>(inputComponent->width) << " HEIGHT: " << static_cast<int>(inputComponent->height) << " INLINE: " << inputComponent->isInline << " Bound: " << inputComponent->boundToPage << " INPUT: " << inputComponent->value << std::endl;
     } else {
         TextComponent *textComponent = dynamic_cast<TextComponent*>(component.get());
         if (textComponent) {
diff --git a/src/graphics/components/Component.h b/src/graphics/components/Component.h
index 2440a444e61940f2c53c37f4f89d1cacdf4334b3..ceb544c5dce73f3cdccedb323bb19907bdf91efa 100644
--- a/src/graphics/components/Component.h
+++ b/src/graphics/components/Component.h
@@ -167,7 +167,10 @@ public:
     std::function<void(int x, int y)> onMouseup = nullptr;
     std::function<void(int x, int y)> onMousemove = nullptr;
     std::function<void(int x, int y)> onWheel = nullptr;
+    // https://www.quirksmode.org/dom/events/keys.html
     std::function<void(int key, int scancode, int action, int mods)> onKeyup = nullptr;
+    // "Fires when an actual character is being inserted in, for instance, a text input. It repeats while the user keeps the key depressed."
+    std::function<void(int key, int scancode, int action, int mods)> onKeyPress = nullptr;
     std::function<void()> onClick = nullptr;
     std::function<void()> onFocus = nullptr;
     std::function<void()> onBlur = nullptr;
diff --git a/src/graphics/components/ComponentBuilder.cpp b/src/graphics/components/ComponentBuilder.cpp
index 07c187c6a387e772dc982c22aaf7bedf868749ce..819eef603d868b9da7739e7b20413f3ed4a0fbb6 100644
--- a/src/graphics/components/ComponentBuilder.cpp
+++ b/src/graphics/components/ComponentBuilder.cpp
@@ -95,6 +95,8 @@ std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> n
     if (inputComponent) {
         // any input set up we need to do?
         // boundToPage defaults to true for components, InputComponent doesn't have it overridded
+        //std::cout << "Just built an inputComponent" << std::endl;
+        inputComponent->win = window.get();
     }
     //std::cout << "composting component, initial: " << (int)component->width << "x" << (int)component->height << std::endl;
     
@@ -123,7 +125,10 @@ std::shared_ptr<Component> ComponentBuilder::build(const std::shared_ptr<Node> n
         std::cout << "ComponentBuilder::build - Just laid out input component" << std::endl;
     }
     */
-    
+    // not sure why only this component needs this but it fixes it
+    if (inputComponent) {
+        inputComponent->updateParentSize();
+    }
     //std::cout << "post layout placed: " << (int)component->x << "x" << (int)component->y << " w/h: " << (int)component->width << "x" << (int)component->height << std::endl;
     return component;
 }
@@ -148,3 +153,20 @@ std::string typeOfComponent(const std::shared_ptr<Component> &component) {
     if (boxComponent) return "box";
     return "";
 }
+
+std::string typeOfComponent(Component *component) {
+    TabbedComponent *tabComponent = dynamic_cast<TabbedComponent*>(component);
+    if (tabComponent) return "tab";
+    DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(component);
+    if (docComponent) return "doc";
+    TextComponent *textComponent = dynamic_cast<TextComponent*>(component);
+    if (textComponent) return "text";
+    InputComponent *inputComponent = dynamic_cast<InputComponent*>(component);
+    if (inputComponent) return "input";
+    AnimeComponent *animeComponent = dynamic_cast<AnimeComponent*>(component);
+    if (animeComponent) return "anime";
+    BoxComponent *boxComponent = dynamic_cast<BoxComponent*>(component);
+    if (boxComponent) return "box";
+    return "";
+}
+
diff --git a/src/graphics/components/ComponentBuilder.h b/src/graphics/components/ComponentBuilder.h
index ea6f79e0f8693dd779e9529e201c7ac5233abf15..b735b32cc037934a72e19ce58408966f9407c68c 100644
--- a/src/graphics/components/ComponentBuilder.h
+++ b/src/graphics/components/ComponentBuilder.h
@@ -21,5 +21,6 @@ public:
 
 // getComponentType
 std::string typeOfComponent(const std::shared_ptr<Component> &component);
+std::string typeOfComponent(Component *component);
 
 #endif
diff --git a/src/graphics/components/DocumentComponent.cpp b/src/graphics/components/DocumentComponent.cpp
index 9a4f838cacc614e117eab19ffb8f17aba54dcb73..3535fd6c2693f0b33427bb9764ec9c662e034658 100644
--- a/src/graphics/components/DocumentComponent.cpp
+++ b/src/graphics/components/DocumentComponent.cpp
@@ -111,7 +111,7 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
         //this->renderDirty = true;
     };
     onMousedown=[this](int passedX, int passedY) {
-        std::cout << "document left press" << std::endl;
+        //std::cout << "document left press" << std::endl;
         if (this->hoverComponent) {
             if (this->focusedComponent != this->hoverComponent) {
                 // blur old component
@@ -133,7 +133,7 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
         }
     };
     onMouseup=[this](int passedX, int passedY) {
-        std::cout << "document left release" << std::endl;
+        //std::cout << "document left release" << std::endl;
         if (this->hoverComponent) {
             //std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component" << std::endl;
             if (this->focusedComponent != this->hoverComponent) {
@@ -149,6 +149,7 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
                 }
             }
             this->focusedComponent = this->hoverComponent;
+            //std::cout << "DocumentComponent::DocumentComponent:onMouseup - hovering over " << typeOfComponent(this->hoverComponent) << " component, focused on " << typeOfComponent(this->focusedComponent) << std::endl;
             if (this->focusedComponent->onMouseup) {
                 //std::cout << "click event" << std::endl;
                 this->focusedComponent->onMouseup(passedX, passedY);
@@ -160,7 +161,7 @@ DocumentComponent::DocumentComponent(const float rawX, const float rawY, const f
         }
     };
     onKeyup=[this](int key, int scancode, int action, int mods) {
-        //std::cout << "DocumentComponent::DocumentComponent:onKeyup" << std::endl;
+        //std::cout << "DocumentComponent::DocumentComponent:onKeyup" << typeOfComponent(this->focusedComponent) << std::endl;
         InputComponent *inputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
         if (inputComponent) {
             //std::cout << "inputComponent is focused, key pressed " << key << " action: " <<action << std::endl;
@@ -318,7 +319,7 @@ std::shared_ptr<Component> DocumentComponent::searchComponentTree(const std::sha
         if (-component->y < passedY && -component->y + component->height > passedY) {
             //std::cout << "DocumentComponent::searchComponentTree - x search: " << static_cast<int>(component->x) << "<" << static_cast<int>(passedX) << "<" << static_cast<int>(component->x + component->width) << std::endl;
             if (component->x < passedX && component->x + component->width > passedX) {
-                //std::cout << "hit " << typeOfComponent(component) << std::endl;
+                //std::cout << "DocumentComponent::searchComponentTree - hit " << typeOfComponent(component) << std::endl;
                 return component;
             }
         }
diff --git a/src/graphics/components/InputComponent.cpp b/src/graphics/components/InputComponent.cpp
index 06757ad21707800682d0e38de20b577d0a058ed1..dc9d326efc1d433ad13303f8acbe659f8ba31ad6 100644
--- a/src/graphics/components/InputComponent.cpp
+++ b/src/graphics/components/InputComponent.cpp
@@ -1,14 +1,15 @@
 #include "InputComponent.h"
 #include <iostream>
-#include "../opengl/Window.h"
 #include "../text/TextRasterizerCache.h"
+#include "../../scheduler.h"
 
 extern TextRasterizerCache *rasterizerCache;
+extern std::unique_ptr<Scheduler> scheduler;
 
 // : BoxComponent(rawX, rawY, rawWidth, rawHeight, passedWindowWidth, passedWindowHeight)
 InputComponent::InputComponent(const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int passedWindowWidth, const int passedWindowHeight) {
 
-    //std::cout << "InputComponent::InputComponent - data" << std::endl;
+    //std::cout << "InputComponent::InputComponent - create, boundToPage" << boundToPage << std::endl;
     //std::cout << "InputComponent::InputComponent - window: " << windowWidth << "x" << windowHeight << " passed " << passedWindowWidth << "x" << passedWindowHeight << std::endl;
     
     //boundToPage = true;
@@ -17,12 +18,41 @@ InputComponent::InputComponent(const float rawX, const float rawY, const float r
     // set up state
     windowWidth = passedWindowWidth;
     windowHeight = passedWindowHeight;
+    // lets not set, like resize set, and then maybe parent width will be correct
+    // didn't really changed anything
     width = rawWidth;
     height = rawHeight;
     // ugh
     x = rawX;
     y = rawY;
     lastRenderedWindowHeight = windowHeight;
+    
+    // const float rawX, const float rawY, const float rawWidth, const float rawHeight, const unsigned int hexColor, const int passedWindowWidth, const int passedWindowHeight
+    this->cursorBox = new BoxComponent(x, y, 2, rawHeight, 0x000000FF, windowWidth, windowHeight);
+    this->cursorBox->boundToPage = this->boundToPage;
+
+    this->cursorBox->x = x;
+    this->cursorBox->y = y;
+    if (boundToPage) {
+        this->cursorBox->y = this->windowHeight + y - 13;
+    }
+    this->cursorBox->resize(this->windowWidth, this->windowHeight);
+    this->updateText();
+    
+    onFocus=[this]() {
+        this->focused = true;
+        this->cursorBox->x = x ;
+        this->cursorBox->y = y;
+        if (this->boundToPage) {
+            this->cursorBox->y = this->windowHeight + y - 13;
+        }
+        this->cursorBox->resize(this->windowWidth, this->windowHeight);
+        this->win->renderDirty = true;
+    };
+    onBlur=[this]() {
+        this->focused = false;
+        this->win->renderDirty = true;
+    };
 
     //std::cout << "InputComponent::InputComponent - placing box at " << (int)x << "," << (int)y << " size: " << (int)width << "x" << (int)height << std::endl;
     
@@ -90,7 +120,8 @@ InputComponent::InputComponent(const float rawX, const float rawY, const float r
 }
 
 void InputComponent::render() {
-    //std::cout << "InputComponent::render" << std::endl;
+    //std::cout << "InputComponent::render - at " << (int)x << "," << (int)y << std::endl;
+    //std::cout << "InputComponent::render - boundToPage " << boundToPage << std::endl;
     GLenum glErr=glGetError();
     if(glErr != GL_NO_ERROR) {
         std::cout << "InputComponent::render - start not ok: " << glErr << std::endl;
@@ -135,6 +166,7 @@ void InputComponent::render() {
             if(glErr != GL_NO_ERROR) {
                 std::cout << "InputComponent::render - glGetUniformLocation not ok: " << glErr << std::endl;
             }
+            // it's about the document transformMatrix
             glUniformMatrix4fv(transformLocation, 1, GL_FALSE, window->transformMatrix);
             glErr=glGetError();
             if(glErr != GL_NO_ERROR) {
@@ -145,10 +177,26 @@ void InputComponent::render() {
         userInputText->render();
         glUseProgram(window->textureProgram);
     }
+    if (focused) {
+        //std::cout << "Rendering cursor" << std::endl;
+        
+        // blink cursor
+        if (cursorTimer != nullptr) {
+            scheduler->clearInterval(cursorTimer);
+        }
+        cursorTimer = scheduler->setInterval([this]() {
+            this->showCursor = !this->showCursor;
+            //std::cout << "showCursor " << this->showCursor << std::endl;
+            this->win->renderDirty = true;
+        }, 500);
+        
+        // render it if we need to
+        if (showCursor) {
+            cursorBox->render();
+        }
+    }
 }
 
-// this doesn't seem to work
-// text is fine though
 void InputComponent::resize(const int passedWindowWidth, const int passedWindowHeight) {
     //std::cout << "InputComponent::resize" << std::endl;
     //std::cout << "InputComponent::resize - rasterizing at " << (int)x << "x" << (int)y << " size: " << (int)width << "x" << (int)height << std::endl;
@@ -157,7 +205,9 @@ void InputComponent::resize(const int passedWindowWidth, const int passedWindowH
     // set up state
     windowWidth = passedWindowWidth;
     windowHeight = passedWindowHeight;
-
+    
+    //std::cout << "InputComponent::resize - boxShader: " << useBoxShader << " boundToPage: " << boundToPage << std::endl;
+    
     /*
     if (!boundToPage) {
         // ok because box shader is anchored to the bottom of the screen
@@ -184,25 +234,11 @@ void InputComponent::resize(const int passedWindowWidth, const int passedWindowH
     
     float vx = x;
     float vy = y;
-    //float vx1 = x + width;
-    //float vy1 = y - height;
-    /*
-    std::cout << "placing box at " << (int)vx << "," << (int)vy << " size: " << (int)width << "x" << (int)height << " v1: " << (int)vx1 << "," << (int)vy1 << std::endl;
-    //float vWidth = width;
-    //float vHeight = height;
-    boundToPage = true;
-    pointToViewport(vx, vy);
-    pointToViewport(vx1, vy1);
-    std::cout << "TRUE placing box at GL v: " << vx << "," << vy << " v1: " << vx1 << "," << vy1 << std::endl;
-    
-    vx = x;
-    vy = y;
-    vx1 = x + width;
-    vy1 = y - height;
-    boundToPage = false;
-    */
+    if (boundToPage) {
+        vy = this->windowHeight + y - height;
+        //std::cout << "InputComponent::resize - Adjust y to " << vy << " from " << y << " h: " << (int)height << std::endl;
+    }
     pointToViewport(vx, vy);
-    //pointToViewport(vx1, vy1);
     
     float vWidth = width;
     float vHeight = height;
@@ -210,15 +246,8 @@ void InputComponent::resize(const int passedWindowWidth, const int passedWindowH
     float vx1 = vx + vWidth;
     float vy1 = vy + vHeight;
     
-    //std::cout << "FALSE placing box at GL v: " << vx << "," << vy << " v1: " << vx1 << "," << vy1 << std::endl;
     //std::cout << "InputComponent::resize - placing box at GL " << vx << "," << vy << " to " << vx1 << "," << vy1 << " size: " << vWidth << "x" << vHeight << std::endl;
     
-    // converts 512 to 1 and 1 to 2
-    //std::cout << "vWidth before: " << (int)vWidth << std::endl;
-    //distanceToViewport(vWidth, vHeight);
-    //std::cout << "vWidth after: " << (int)vWidth << std::endl;
-    
-    
     vertices[(0 * 5) + 0] = vx;
     vertices[(0 * 5) + 1] = vy1;
 
@@ -232,19 +261,15 @@ void InputComponent::resize(const int passedWindowWidth, const int passedWindowH
     vertices[(3 * 5) + 1] = vy;
     
     glBindVertexArray(vertexArrayObject);
-    
     glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
-    
-    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject);
-    //glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
-    
-   
     glBindVertexArray(0); // protect what we created against any further modification
     
     //updateText();
     if (userInputText) {
+        // do we need updateText here?
         userInputText->resize(passedWindowWidth, passedWindowHeight);
+        
     }
 }
 
@@ -273,6 +298,7 @@ void InputComponent::updateText() {
     } else {
         userInputText->y = y - windowHeight + 16;
     }
+    userInputText->boundToPage = this->boundToPage;
     //std::cout << "placed userInputText at " << static_cast<int>(x) << "," << static_cast<int>(y - windowHeight) << std::endl;
     // 125 pixels width
     // but first we need to know how wide the text is
@@ -299,6 +325,16 @@ void InputComponent::updateText() {
         //std::cout << "scrolling text" << std::endl;
         userInputText->rasterStartX = estWidth - width;
     }
+    // this is texture shader coordinates now (not text shader coords)
+    cursorBox->x = x + estWidth;
+    if (boundToPage) {
+        cursorBox->y = windowHeight + y - 13;
+    } else {
+        cursorBox->y = y;
+    }
+    //std::cout << "placing cursor at " << (int)cursorBox->x << "," << (int)cursorBox->y << std::endl;
+    cursorBox->resize(windowWidth, windowHeight);
+    
     userInputText->noWrap = true;
     // why does changing the width mess shit up?
     //std::cout << "InputComponent::updateText - our width: " << static_cast<int>(width) << " windowWidth: " << windowWidth << std::endl;
diff --git a/src/graphics/components/InputComponent.h b/src/graphics/components/InputComponent.h
index 3e7a7b5273e2f77b6cba14cf680fd1a456615794..e6e0d778a4835dba01d7a8097a2f766b9ecb66be 100644
--- a/src/graphics/components/InputComponent.h
+++ b/src/graphics/components/InputComponent.h
@@ -6,6 +6,7 @@
 #include "BoxComponent.h"
 #include "TextComponent.h"
 #include "../opengl/Window.h"
+#include "../../scheduler.h"
 
 class Window;
 
@@ -22,9 +23,13 @@ public:
     void updateText();
     std::string value="";
     TextComponent *userInputText = nullptr;
+    BoxComponent *cursorBox = nullptr;
     std::function<void(std::string value)> onEnter = nullptr;
     // needed for our shader's resizing
     int lastRenderedWindowHeight;
+    bool focused = false;
+    bool showCursor = true;
+    std::shared_ptr<timer_handle> cursorTimer = nullptr;
     // handle to signal that a redraw is needed, and access to shader programs
     Window *win;
 };
diff --git a/src/graphics/components/MultiComponent.cpp b/src/graphics/components/MultiComponent.cpp
index 7b1e6458229aa5ecf664cf599ffef37ef57df99d..ba44b4fbb25b94893cc3598a1a82a1504ffe1408 100644
--- a/src/graphics/components/MultiComponent.cpp
+++ b/src/graphics/components/MultiComponent.cpp
@@ -111,7 +111,16 @@ MultiComponent::MultiComponent(const float rawX, const float rawY, const float r
         // should we mark win->renderDirty = true?
     };
     onKeyup=[this](int key, int scancode, int action, int mods) {
-        //std::cout << "MultiComponent::MultiComponent:onKeyup" << std::endl;
+        //std::cout << "MultiComponent::MultiComponent:onKeyup - focused on " << typeOfComponent(this->focusedComponent) << std::endl;
+        DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(this->focusedComponent.get());
+        if (docComponent) {
+            if (action == 0) {
+                if (docComponent->onKeyup) {
+                    docComponent->onKeyup(key, scancode, action, mods);
+                }
+            }
+            return;
+        }
         InputComponent *inputComponent = dynamic_cast<InputComponent*>(this->focusedComponent.get());
         if (inputComponent) {
             //std::cout << "inputComponent is focused, key pressed " << key << " action: " <<action << std::endl;
@@ -216,12 +225,12 @@ void MultiComponent::render() {
         GLint transformLocation = glGetUniformLocation(win->fontProgram, "transform");
         GLenum glErr=glGetError();
         if(glErr != GL_NO_ERROR) {
-            std::cout << "InputComponent::render - glGetUniformLocation not ok: " << glErr << std::endl;
+            std::cout << "MultiComponent::render - glGetUniformLocation not ok: " << glErr << std::endl;
         }
         glUniformMatrix4fv(transformLocation, 1, GL_FALSE, win->transformMatrix);
         glErr=glGetError();
         if(glErr != GL_NO_ERROR) {
-            std::cout << "InputComponent::render - glUniformMatrix4fv not ok: " << glErr << std::endl;
+            std::cout << "MultiComponent::render - glUniformMatrix4fv not ok: " << glErr << std::endl;
         }
     }
     renderComponents(rootComponent);
@@ -294,7 +303,7 @@ void MultiComponent::renderBoxComponents(std::shared_ptr<Component> component) {
 
 void MultiComponent::renderComponentType(std::string str, std::shared_ptr<Component> component) {
     if (!component) {
-        std::cout << "Window::renderComponentType - got null passed" << std::endl;
+        std::cout << "MultiComponent::renderComponentType - got null passed" << std::endl;
         return;
     }
     if (typeOfComponent(component) == str) {
diff --git a/src/graphics/components/MultiComponent.h b/src/graphics/components/MultiComponent.h
index 9cba4faa19a83700391bd46b4ac366b4a339d356..e58d42429638977a9fb45ac0660d48bcc8882aec 100644
--- a/src/graphics/components/MultiComponent.h
+++ b/src/graphics/components/MultiComponent.h
@@ -50,6 +50,6 @@ public:
     double cursorY = 0;
 };
 
-extern const std::unique_ptr<Window> window;
+//extern const std::unique_ptr<Window> window;
 
 #endif
diff --git a/src/graphics/components/TabbedComponent.cpp b/src/graphics/components/TabbedComponent.cpp
index e84cdbe16314e0ab78fd9f32947a7681f20e68fd..d38a68738699a06ffb8b76f77d30e7a1556abf91 100644
--- a/src/graphics/components/TabbedComponent.cpp
+++ b/src/graphics/components/TabbedComponent.cpp
@@ -355,11 +355,11 @@ void TabbedComponent::selectTab(std::shared_ptr<Tab> tab) {
 // we're not going to do a full set up uiContorl, we'll expect those to be set up and just adjust them
 // but then, we can't just call this to place things where we want in the cstr... hrm..
 void TabbedComponent::layoutTab(std::vector<std::shared_ptr<Tab>>::iterator tab) {
-    std::cout << "TabbedComponent::layoutTab - id: " << tab->get()->id << std::endl;
+    //std::cout << "TabbedComponent::layoutTab - id: " << tab->get()->id << std::endl;
     // find text
     TextComponent *textComponent = tab->get()->titleBox.get();
     if (!textComponent) {
-        std::cout << "TabbedComponent::loadDomIntoTab - titleBox isn't a TextComponent" << std::endl;
+        std::cout << "TabbedComponent::layoutTab - titleBox isn't a TextComponent" << std::endl;
         return;
     }
     
@@ -369,13 +369,13 @@ void TabbedComponent::layoutTab(std::vector<std::shared_ptr<Tab>>::iterator tab)
     //Tab *prev = it->get();
     Tab *prev = tab->get()->previousTab.get();
     if (prev == nullptr) {
-        std::cout << "relaying out first tab, no prev" << std::endl;
+        //std::cout << "TabbedComponent::layoutTab - relaying out first tab, no prev" << std::endl;
         //prev = nullptr;
         tab->get()->x = x + 32; // our start + 32px for new tab button
     } else {
         tab->get()->x = prev->x + static_cast<int>(prev->w);
     }
-    std::cout << "placing tab at " << tab->get()->x << std::endl;
+    //std::cout << "TabbedComponent::layoutTab - placing tab at " << tab->get()->x << std::endl;
     
     // get text size for it's current string
     // we don't always need to adjust textWidth, only on text change
@@ -417,7 +417,7 @@ void TabbedComponent::layoutTab(std::vector<std::shared_ptr<Tab>>::iterator tab)
 
 // maybe take in a starting tab? as tabs to left aren't affected
 void TabbedComponent::layoutTabs(std::vector<std::shared_ptr<Tab>>::iterator startTab, int xAdj) {
-    std::cout << "TabbedComponent::layoutTabs - startId: " << startTab->get()->id << " xadj: " << xAdj << std::endl;
+    //std::cout << "TabbedComponent::layoutTabs - startId: " << startTab->get()->id << " xadj: " << xAdj << std::endl;
     // luckily only one component at a time changes, we'll need to know how much x shifted
     for(std::vector<std::shared_ptr<Tab>>::iterator it = startTab; it!=this->tabs.end(); ++it) {
         //it->get()->x += xAdj; // adjust position
diff --git a/src/graphics/components/TextComponent.cpp b/src/graphics/components/TextComponent.cpp
index a845488edaede0c21fbe946464954259100ae76d..60d3c39d9209a10b99a54288c046d6da451aead6 100644
--- a/src/graphics/components/TextComponent.cpp
+++ b/src/graphics/components/TextComponent.cpp
@@ -110,14 +110,11 @@ void TextComponent::rasterize(const int rawX, const int rawY) {
     //glyphs = textRasterizer->rasterize(text, rawX, windowWidth, wrapToX, width, height, glyphCount, endingX, endingY, wrapped);
     rasterizationRequest request;
     request.text = text;
-    // startX needs to be relative to the parent x. Why?
-    // I think for non-boundComponents
     request.startX = rawX; // - x
-    /*
     if (!boundToPage) {
+        // startX needs to be relative to the parent x. Why?
         request.startX -= x;
     }
-    */
     //std::cout << "TextComponent::rasterize - [" << text << "] request.startX: " << request.startX << " x: " << x << " rawX: " << rawX << std::endl;
     request.availableWidth = availableWidth;
     request.sourceStartX = rasterStartX;
@@ -126,7 +123,8 @@ void TextComponent::rasterize(const int rawX, const int rawY) {
     //std::cout << "rasterizing [" << text << "] @" << rawX << " availableWidth: " << availableWidth << " sourceStartX: " << rasterStartX << " noWrap: " << noWrap << std::endl;
     std::shared_ptr<rasterizationResponse> response = textRasterizer->rasterize(request);
     if (response.get() == nullptr) {
-        std::cout << "TextComponent::rasterize - got nullptr from rasterizer" << std::endl;
+        // window could be minimized
+        //std::cout << "TextComponent::rasterize - got nullptr from rasterizer" << std::endl;
         return;
     }
     width = response->width;
diff --git a/src/graphics/opengl/Window.cpp b/src/graphics/opengl/Window.cpp
index b486406ace7eeabc3854d5bf1bef09bb786b285a..b7dce38c8ca73f9cd7db53041533f75fef0b9ea5 100644
--- a/src/graphics/opengl/Window.cpp
+++ b/src/graphics/opengl/Window.cpp
@@ -207,6 +207,7 @@ bool Window::init() {
     navAddressBar->uiControl.h = {   0,   24 }; // 24px
     navAddressBar->name = "navAddressBar";
     navAddressBar->boundToPage = false;
+    navAddressBar->win = this;
     //navAddressBar->y = -48; // this works but breaks picking
     navAddressBar->value = currentURL.toString();
     navAddressBar->updateText();
@@ -401,10 +402,12 @@ bool Window::initGLFW() {
                     }
                 }
                 thiz->focusedComponent = thiz->hoverComponent;
+                /*
                 InputComponent *inputComponent = dynamic_cast<InputComponent*>(thiz->focusedComponent.get());
                 if (inputComponent) {
                     std::cout << "inputComponent focus" << std::endl;
                 }
+                 */
                 if (thiz->focusedComponent && thiz->focusedComponent->onMouseup) {
                     //std::cout << "click event" << std::endl;
                     thiz->focusedComponent->onMouseup(thiz->cursorX, thiz->cursorY);
@@ -428,7 +431,25 @@ bool Window::initGLFW() {
     glfwSetKeyCallback(window, [](GLFWwindow *win, int key, int scancode, int action, int mods) {
         Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
         // we're focused on something
+        //std::cout << "glfwSetKeyCallback" << std::endl;
         if (thiz->focusedComponent) {
+            //std::cout << "glfwSetKeyCallback - focused on " << typeOfComponent(thiz->focusedComponent) << std::endl;
+            TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(thiz->focusedComponent.get());
+            if (p_tabComponent) {
+                // repeat or key up
+                if (action == 2 || action == 0) {
+                    if (p_tabComponent->onKeyPress) {
+                        p_tabComponent->onKeyPress(key, scancode, action, mods);
+                    }
+                    if (action == 0) {
+                        if (p_tabComponent->onKeyup) {
+                            p_tabComponent->onKeyup(key, scancode, action, mods);
+                        }
+                    }
+                }
+                return;
+            }
+            
             DocumentComponent *docComponent = dynamic_cast<DocumentComponent*>(thiz->focusedComponent.get());
             if (docComponent) {
                 if (action == 0) {
@@ -526,7 +547,11 @@ bool Window::initGLFW() {
             }
             printf("\n\n");
         }
-
+        if (key == GLFW_KEY_F && action == GLFW_RELEASE) {
+            printf("Printing UI ComponentTree\n\n");
+            Component::printComponentTree(thiz->rootComponent, 0);
+            printf("\n\n");
+        }
         int yOffsetScroll = 1;
         if (key == GLFW_KEY_PAGE_UP && (action == GLFW_PRESS || action == GLFW_REPEAT)) {
             TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
@@ -634,7 +659,9 @@ bool Window::initGL() {
     }
 
     //std::cout << "OpenGL is set up" << std::endl;
-
+    
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+    
     return true;
 }
 
diff --git a/src/graphics/opengl/Window.h b/src/graphics/opengl/Window.h
index 38ff8a39cb7babedbb74451ecd46258df452b1be..8d67109171b1719e12e458cfa1a82018c14d8b2f 100644
--- a/src/graphics/opengl/Window.h
+++ b/src/graphics/opengl/Window.h
@@ -39,6 +39,7 @@ public:
     void navTo(std::string url);
 
     // properties
+    int maxTextureSize = 0;
     float transformMatrix[16] = {
         1, 0, 0, 0,
         0, 1, 0, 0,
diff --git a/src/graphics/text/TextRasterizer.cpp b/src/graphics/text/TextRasterizer.cpp
index b1698f30b311195ce9c79e88e5cdcecb9a2a2e2d..735a46b6f52538042c5f58b8e0bb6ffefa7cbb4b 100644
--- a/src/graphics/text/TextRasterizer.cpp
+++ b/src/graphics/text/TextRasterizer.cpp
@@ -37,7 +37,10 @@ TextRasterizer::~TextRasterizer() {
 }
 
 std::unique_ptr<std::pair<int, int>> TextRasterizer::size(const rasterizationRequest &request) const {
-    
+    if (!request.availableWidth) {
+        // window is likely minimized, so no output
+        return nullptr;
+    }
     if (request.startX == request.availableWidth) {
         std::cout << "TextRasterizer::size - x [" << static_cast<int>(request.startX) << "] matches window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "] no room to render anything" << std::endl;
         return nullptr;
@@ -151,6 +154,7 @@ std::unique_ptr<std::pair<int, int>> TextRasterizer::size(const rasterizationReq
         response->height = y1max;
         //std::cout << "adjusted:" << (int)width << "x" << (int)height << std::endl;
     }
+    // FIXME: how about endingX,Y?
     return std::make_unique<std::pair<int, int>>(std::pair<int, int>(response->width, response->height));
 }
 
@@ -310,12 +314,10 @@ std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasteriza
     response->y1 = -response->height;
     //std::cout << "xd: " << static_cast<int>(response->x1-response->x0) << " yd: " << static_cast<int>(response->y0-response->y1) << std::endl;
     
-    GLint max;
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
-    std::cout << "Requesting texture of " << response->textureWidth << "x" << response->textureHeight << "max: " << max << std::endl;
-    if (response->textureHeight > static_cast<unsigned int>(max)) {
+    //std::cout << "Requesting texture of " << response->textureWidth << "x" << response->textureHeight << "max: " << win->maxTextureSize << std::endl;
+    if (response->textureHeight > static_cast<unsigned int>(win->maxTextureSize)) {
         std::cout << "Truncating text texture to fit in your video card" << std::endl;
-        response->textureHeight = static_cast<unsigned int>(max);
+        response->textureHeight = static_cast<unsigned int>(win->maxTextureSize);
     }
     
     // texture coords
diff --git a/src/graphics/text/TextRasterizer.h b/src/graphics/text/TextRasterizer.h
index 5a908adab413f698d7ffdeee1f017c9141636584..bbe66546fff9d8e01291d3e7ee039ac417a82d00 100644
--- a/src/graphics/text/TextRasterizer.h
+++ b/src/graphics/text/TextRasterizer.h
@@ -6,6 +6,7 @@
 #include <memory>
 #include <string>
 #include <algorithm>
+#include "../opengl/Window.h"
 
 struct Glyph {
     float x0;
@@ -76,6 +77,7 @@ public:
     hb_font_t *font;
     hb_buffer_t *buffer;
     std::unique_ptr<FT_Face> face;
+    Window *win;
 };
 
 #endif
diff --git a/src/graphics/text/TextRasterizerCache.cpp b/src/graphics/text/TextRasterizerCache.cpp
index 092a62d93f8354222921cc8b8b4a635362255c80..14b94b63660aca81a457659059951b7a406bd987 100644
--- a/src/graphics/text/TextRasterizerCache.cpp
+++ b/src/graphics/text/TextRasterizerCache.cpp
@@ -1,7 +1,10 @@
 #include "TextRasterizerCache.h"
+#include <iostream>
 
 TextRasterizerCache *rasterizerCache=new TextRasterizerCache;
 
+extern const std::unique_ptr<Window> window;
+
 // reduces this:
 // Updated DOM in: 6.787870 seconds
 // to this:
@@ -9,14 +12,18 @@ TextRasterizerCache *rasterizerCache=new TextRasterizerCache;
 std::shared_ptr<TextRasterizer> TextRasterizerCache::loadFont(const unsigned int size, const bool bold) {
     if (bold) {
         if (fontSizes_bold.find(size) == fontSizes_bold.end()) {
+            //std::cout << "Making bold font" << std::endl;
             fontSizes_bold[size]=std::make_shared<TextRasterizer>("DejaVuSerif.ttf", size, 72, bold);
+            fontSizes_bold[size]->win = window.get();
         }
         return fontSizes_bold[size];
     }
     else {
         if (fontSizes_notbold.find(size) == fontSizes_notbold.end()) {
+            //std::cout << "Making font" << std::endl;
             fontSizes_notbold[size]=std::make_shared<TextRasterizer>("DejaVuSerif.ttf", size, 72, bold);
+            fontSizes_notbold[size]->win = window.get();
         }
         return fontSizes_notbold[size];
     }
-}
\ No newline at end of file
+}
diff --git a/src/main.cpp b/src/main.cpp
index 680cdb5a79b5c79bff1749fce7e5b6dace91350e..84929419e5dcb5f707aae5e7e01ea1b79133955a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,6 +5,8 @@
 #include "URL.h"
 #include "WebResource.h"
 #include "tlsf.h"
+#include "scheduler.h"
+
 #include <ctime>
 #include <iostream>
 #include <sys/stat.h>
@@ -21,8 +23,11 @@ extern "C"{
 #endif
 
 const std::unique_ptr<Window> window = std::make_unique<Window>();
+// why can't I const this?
+std::unique_ptr<Scheduler> scheduler = std::make_unique<Scheduler>();
 //URL currentURL;
 
+
 bool setWindowContent(URL const& url) {
     logDebug() << "main::setWindowContent - " << url << std::endl;
 
@@ -47,17 +52,19 @@ bool setWindowContent(URL const& url) {
         std::cout << "Rendering text document" << std::endl;
         std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT);
         std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
+        tagNode->tag="p";
+        // bind tag to root
+        tagNode->parent = rootNode;
+        rootNode->children.push_back(tagNode);
+        
+        
         std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>();
         textNode->text = res.raw;
-        tagNode->tag="p";
         
         // bind text to tag
         textNode->parent = tagNode;
         tagNode->children.push_back(textNode);
         
-        // bind tag to root
-        tagNode->parent = rootNode;
-        rootNode->children.push_back(tagNode);
         // send NodeTree to window
         window->setDOM(rootNode);
     } else {
@@ -124,11 +131,19 @@ int main(int argc, char *argv[]) {
         }
     }
     
-    
     while (!glfwWindowShouldClose(window->window)) {
         //const std::clock_t begin = clock();
         window->render();
-        glfwWaitEvents(); // block until something changes
+        scheduler->fireTimers(); // render may have taken some time
+        double next = scheduler->getNext();
+        //std::cout << "next timer at " << next << std::endl;
+        if (next == LONG_MAX) {
+          glfwWaitEvents(); // block until something changes
+        } else {
+          glfwWaitEventsTimeout(next / 1000);
+        }
+        scheduler->fireTimers(); // check before we go into render again
+        //glfwWaitEventsTimeout(1.0 / 60.0); // increase the cpu from 0% to 2% on idle
         //const std::clock_t end = clock();
         //std::cout << '\r' << std::fixed << (((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) * 1000) << std::scientific << " ms/f    " << std::flush;
     }
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5e2b30ea795d3330584147981628bdf72ec9af03
--- /dev/null
+++ b/src/scheduler.cpp
@@ -0,0 +1,77 @@
+#include "scheduler.h"
+#include <GLFW/glfw3.h>
+#include <iostream>
+#include <algorithm>
+
+std::shared_ptr<timer_handle> Scheduler::setTimeout(std::function<void()> callback, unsigned long delay) {
+    double now = glfwGetTime() * 1000;
+    std::shared_ptr<timer_handle> handle = std::make_shared<timer_handle>();
+    handle->callback = callback;
+    handle->nextAt = now + delay;
+    handle->timeout = true;
+    this->timers.push_back(handle);
+    return handle;
+}
+
+std::shared_ptr<timer_handle> Scheduler::setInterval(std::function<void()> callback, unsigned long delay) {
+    double now = glfwGetTime() * 1000;
+    //std::cout << "Scheduler::setInterval at " << (int)now << std::endl;
+    std::shared_ptr<timer_handle> handle = std::make_shared<timer_handle>();
+    handle->callback = callback;
+    handle->nextAt = now + delay;
+    handle->timeout = false;
+    this->timers.push_back(handle);
+    //std::cout << "scheduled at " << (int)handle->nextAt << std::endl;
+    return handle;
+}
+
+unsigned long Scheduler::getNext() {
+    unsigned long lowest = LONG_MAX;
+    double now = glfwGetTime() * 1000; // it's number of ms since start up
+    //std::cout << "there are " << this->timers.size() << std::endl;
+    for(std::vector<std::shared_ptr<timer_handle>>::iterator it=this->timers.begin(); it!=this->timers.end(); ++it) {
+        //std::cout << "nextAt " << (int)it->get()->nextAt << " long " << (int)(now - it->get()->nextAt) << std::endl;
+        //std::cout << "now " << (int)now << " nextAt: " << (int)it->get()->nextAt << std::endl;
+        int diff = now - it->get()->nextAt;
+        if (diff < 0) diff = 0;
+        //std::cout << "timer fires in " << diff << std::endl;
+        lowest = std::min(lowest, static_cast<unsigned long>(diff));
+    }
+    //std::cout << "returning " << lowest << std::endl;
+    return lowest;
+}
+
+bool Scheduler::fireTimer(std::shared_ptr<timer_handle> timer, double now) {
+    timer->callback();
+    if (timer->timeout) {
+        return true;
+    }
+    // schedule the next one
+    timer->nextAt = now + timer->nextAt;
+    return false;
+}
+
+void Scheduler::fireTimers() {
+    double now = glfwGetTime() * 1000;
+    for(std::vector<std::shared_ptr<timer_handle>>::iterator it=this->timers.begin(); it!=this->timers.end(); ++it) {
+        if (it->get()->nextAt < now) {
+            if (this->fireTimer(*it, now)) {
+                it = this->timers.erase(it);
+                if (it == this->timers.end()) break;
+            }
+        }
+    }
+}
+
+bool Scheduler::clearInterval(std::shared_ptr<timer_handle> timer) {
+    // find and remove it
+    //it2 = std::find(this->rootComponent->children.begin(), this->rootComponent->children.end(), it->get()->selectorBox);
+    std::vector<std::shared_ptr<timer_handle>>::iterator it = std::find(this->timers.begin(), this->timers.end(), timer);
+    if (it != this->timers.end()) {
+        //std::cout << "Clearing Interval" << std::endl;
+        this->timers.erase(it);
+    } else {
+        std::cout << "Couldnt find timer" << std::endl;
+    }
+    return true;
+}
diff --git a/src/scheduler.h b/src/scheduler.h
new file mode 100644
index 0000000000000000000000000000000000000000..e02d37286726272a962c41b6faf5613b60a517b9
--- /dev/null
+++ b/src/scheduler.h
@@ -0,0 +1,27 @@
+#ifndef SCHEDULER_H
+#define SCHEDULER_H
+
+#include <memory>
+#include <vector>
+#include <functional>
+#include <limits.h>
+
+class timer_handle {
+public:
+    double nextAt; // in milliseconds
+    bool timeout;
+    std::function<void()> callback;
+};
+
+class Scheduler {
+public:
+    std::shared_ptr<timer_handle> setTimeout(std::function<void()> callback, unsigned long delay);
+    std::shared_ptr<timer_handle> setInterval(std::function<void()> callback, unsigned long delay);
+    unsigned long getNext();
+    bool fireTimer(std::shared_ptr<timer_handle> timer, double now);
+    void fireTimers();
+    bool clearInterval(std::shared_ptr<timer_handle> timer);
+    std::vector<std::shared_ptr<timer_handle>> timers;
+};
+
+#endif