diff --git a/package.json b/package.json
index 748c9fc..0efdbb4 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
     "description": "A template for writing jam games in HTML5 + TypeScript",
     "main": "./src/index.ts",
     "scripts": {
-        "start": "webpack-dev-server --open --mode development",
+        "dev": "webpack-dev-server --open --mode development",
         "build": "webpack --mode production"
     },
     "repository": {
@@ -32,5 +32,13 @@
         "webpack": "^4.29.5",
         "webpack-cli": "^3.2.3",
         "webpack-dev-server": "^3.2.0"
+    },
+    "dependencies": {
+        "@eix/utils": "git+https://github.com/eix-js/utils.git",
+        "haunted": "^4.3.0",
+        "lit-html": "^1.0.0",
+        "lit-rx": "0.0.2",
+        "prelude-ts": "^0.8.2",
+        "rxjs": "^6.5.2"
     }
 }
diff --git a/src/index.html b/src/index.html
index 83ae23d..3ca22f1 100644
--- a/src/index.html
+++ b/src/index.html
@@ -8,6 +8,7 @@
 </head>
 
 <body>
+    hello world
 </body>
 
 </html>
\ No newline at end of file
diff --git a/src/scss/base.scss b/src/scss/base.scss
index 375a34a..7e88921 100644
--- a/src/scss/base.scss
+++ b/src/scss/base.scss
@@ -1,3 +1,14 @@
-body {
-    padding: 0
+html, body {
+    padding: 0;
+    margin: 0;
+    overflow: hidden;
+    height:100%;
+    display: block;
+}
+
+svg {
+    background-color: #444444;
+    height: 100%;
+    width: 100%;
+    display: block;
 }
\ No newline at end of file
diff --git a/src/ts/common/activation/activationStore.ts b/src/ts/common/activation/activationStore.ts
new file mode 100644
index 0000000..909b2c7
--- /dev/null
+++ b/src/ts/common/activation/activationStore.ts
@@ -0,0 +1,23 @@
+import { Singleton } from "@eix/utils";
+import { activationFunction, activationFunctionParam } from "./interfaces"
+import { toActivationFunction } from "./toActivation";
+
+@Singleton
+export class FunctionStore {
+    functions = new Map<string, activationFunction>()
+
+    private storageKeyword: string
+
+    constructor(name="activation") {
+        this.storageKeyword  =`/${name}`
+        for (let i in localStorage) {
+            if (i.indexOf(this.storageKeyword) == 0)
+                this.register(i.substr(this.storageKeyword.length), localStorage[i])
+        }
+    }
+
+    register(name: string, activation: activationFunctionParam) {
+        this.functions.set(name, toActivationFunction(activation))
+        localStorage[`${this.storageKeyword.substr(1)}/${name}`] = activation
+    }
+}
\ No newline at end of file
diff --git a/src/ts/common/activation/index.ts b/src/ts/common/activation/index.ts
new file mode 100644
index 0000000..ed346eb
--- /dev/null
+++ b/src/ts/common/activation/index.ts
@@ -0,0 +1 @@
+export * from "./toActivation"
\ No newline at end of file
diff --git a/src/ts/common/activation/interfaces.ts b/src/ts/common/activation/interfaces.ts
new file mode 100644
index 0000000..a2ca08e
--- /dev/null
+++ b/src/ts/common/activation/interfaces.ts
@@ -0,0 +1,4 @@
+export type activationInput = any[]
+export type activationOutput = boolean
+export type activationFunctionParam = ( (data:activationInput) => activationOutput ) | string
+export type activationFunction = (data:activationInput) => activationOutput
\ No newline at end of file
diff --git a/src/ts/common/activation/toActivation.ts b/src/ts/common/activation/toActivation.ts
new file mode 100644
index 0000000..ef9a7e5
--- /dev/null
+++ b/src/ts/common/activation/toActivation.ts
@@ -0,0 +1,8 @@
+import { activationFunctionParam, activationFunction } from "./interfaces";
+
+export const toActivationFunction = (original: activationFunctionParam) => {
+    const stringified = (typeof original == "string") ? original : original.toString()
+    const final = new Function(`return ${stringified}`) as () => activationFunction
+
+    return final()
+}
\ No newline at end of file
diff --git a/src/ts/common/clamp/clamp.ts b/src/ts/common/clamp/clamp.ts
new file mode 100644
index 0000000..3ba70be
--- /dev/null
+++ b/src/ts/common/clamp/clamp.ts
@@ -0,0 +1,5 @@
+export default function clamp(value:number, min:number, max:number) {
+    return min < max
+      ? (value < min ? min : value > max ? max : value)
+      : (value < max ? max : value > min ? min : value)
+  }
\ No newline at end of file
diff --git a/src/ts/common/clamp/index.ts b/src/ts/common/clamp/index.ts
new file mode 100644
index 0000000..bddee4f
--- /dev/null
+++ b/src/ts/common/clamp/index.ts
@@ -0,0 +1 @@
+export * from "./clamp"
\ No newline at end of file
diff --git a/src/ts/common/component/component.ts b/src/ts/common/component/component.ts
new file mode 100644
index 0000000..1294ec4
--- /dev/null
+++ b/src/ts/common/component/component.ts
@@ -0,0 +1,76 @@
+import { Vector } from "prelude-ts"
+import { Subject, BehaviorSubject } from "rxjs";
+import { ComponentState } from "./interfaces";
+import { map } from "rxjs/operators";
+import { Screen } from "../screen.ts";
+
+export class Component {
+    private static screen = new Screen()
+
+    public position = new BehaviorSubject<number[]>(null)
+    public scale = new BehaviorSubject<number[]>(null)
+    public clicked = false
+
+    private mouserDelta: number[]
+
+    constructor(public activationType: string,
+        position: [number, number] = [0, 0],
+        scale: [number, number] = [0, 0]) {
+        this.position.next(position)
+        this.scale.next(scale)
+    }
+
+    handleMouseUp(e: MouseEvent) {
+        this.clicked = false
+    }
+
+    move(e: MouseEvent) {
+        const mousePosition = Component.screen.getWorldPosition(e.clientX, e.clientY)
+        this.position.next(mousePosition.map((value, index) =>
+            value - this.mouserDelta[index]
+        ))
+    }
+
+    handleClick(e: MouseEvent) {
+        const mousePosition = Component.screen.getWorldPosition(e.clientX, e.clientY)
+
+        this.mouserDelta = this.position.value.map((value, index) =>
+            mousePosition[index] - value
+        )
+        this.clicked = true
+    }
+
+    get state(): ComponentState {
+        return {
+            position: this.position.value as [number, number],
+            scale: this.position.value as [number, number],
+            activationType: this.activationType
+        }
+    }
+
+    get x() {
+        return this.position.pipe(map(val =>
+            val[0]
+        ))
+    }
+    get y() {
+        return this.position.pipe(map(val =>
+            val[1]
+        ))
+    }
+
+    get width() {
+        return this.scale.pipe(map(val =>
+            val[0]
+        ))
+    }
+    get height() {
+        return this.scale.pipe(map(val =>
+            val[1]
+        ))
+    }
+
+    static fromState(state:ComponentState){
+        return new Component(state.activationType, state.position, state.scale)
+    }
+}
\ No newline at end of file
diff --git a/src/ts/common/component/index.ts b/src/ts/common/component/index.ts
new file mode 100644
index 0000000..7c2d3e4
--- /dev/null
+++ b/src/ts/common/component/index.ts
@@ -0,0 +1 @@
+export * from "./component"
\ No newline at end of file
diff --git a/src/ts/common/component/interfaces.ts b/src/ts/common/component/interfaces.ts
new file mode 100644
index 0000000..6b4ca7a
--- /dev/null
+++ b/src/ts/common/component/interfaces.ts
@@ -0,0 +1,5 @@
+export interface ComponentState {
+    position: [number,number]
+    scale: [number,number]
+    activationType: string
+}
\ No newline at end of file
diff --git a/src/ts/common/componentManager/componentManager.ts b/src/ts/common/componentManager/componentManager.ts
new file mode 100644
index 0000000..21eb38e
--- /dev/null
+++ b/src/ts/common/componentManager/componentManager.ts
@@ -0,0 +1,115 @@
+import { Singleton } from "@eix/utils";
+import { Component } from "../component/component";
+import { Subject } from "rxjs";
+import { svg, SVGTemplateResult } from "lit-html";
+import { subscribe } from "lit-rx";
+import { Screen } from "../screen.ts";
+import { MnanagerState } from "./interfaces";
+
+@Singleton
+export class ComponentManager {
+    public components: Component[] = []
+    public svgs = new Subject<SVGTemplateResult[]>()
+
+    private onTop: Component
+    private clicked = false
+    private screen = new Screen()
+
+    constructor() {
+        this.svgs.next(this.render())
+    }
+
+    update() {
+        this.svgs.next(this.render())
+    }
+
+    handleMouseDown(e: MouseEvent) {
+        this.clicked = true
+    }
+
+    handleMouseUp(e: MouseEvent) {
+        this.clicked = false
+    }
+
+    handleMouseMove(e: MouseEvent) {
+        let toAddOnTop: number
+        let outsideComponents = true
+
+        for (let i = 0; i < this.components.length; i++) {
+            const component = this.components[i]
+            if (component.clicked) {
+                outsideComponents = false
+                component.move(e)
+                if (this.onTop != component) {
+                    toAddOnTop = i
+                }
+            }
+        }
+
+        if (toAddOnTop >= 0) {
+            this.onTop = this.components[toAddOnTop]
+            this.components.push(this.onTop)
+            this.update()
+        }
+
+        else if (outsideComponents && this.clicked) {
+            const mousePosition = [e.clientX, e.clientY]
+            const delta = mousePosition.map((value, index) =>
+                this.screen.mousePosition[index] - value
+            ) as [number, number]
+            this.screen.move(...delta)
+        }
+    }
+
+    render() {
+        let toRemoveDuplicatesFor: Component
+
+        const result = this.components.map(component => svg`
+            <rect width=${ subscribe(component.width)}
+             height=${ subscribe(component.height)} 
+             x=${ subscribe(component.x)}
+             y=${ subscribe(component.y)}
+             fill="red"
+             stroke="black"
+             @mousedown=${ (e: MouseEvent) => component.handleClick(e)}
+             @mouseup=${ (e: MouseEvent) => {
+                component.handleMouseUp(e)
+                toRemoveDuplicatesFor = component
+            }}
+             >
+        `);
+
+        if (toRemoveDuplicatesFor)
+            this.removeDuplicates(toRemoveDuplicatesFor)
+
+        return result
+    }
+
+    private removeDuplicates(component: Component) {
+        let instances = this.components
+            .map((value, index) => (value == component) ? index : null)
+            .filter(value => value)
+        instances.pop()
+
+        this.components = this.components
+            .filter((val, index) => instances.indexOf(index) != -1)
+    }
+
+    get state(): MnanagerState {
+        const components = Array.from((new Set(this.components)).values())
+        return {
+            components: components.map(value => value.state)
+        }
+    }
+
+    loadState(state:MnanagerState) {
+        this.clicked = false
+        this.components = state.components.map(value => Component.fromState(value))
+        this.onTop = null
+        this.update()
+    }
+
+    save(){
+        //TODO: implement
+    }
+}
\ No newline at end of file
diff --git a/src/ts/common/componentManager/index.ts b/src/ts/common/componentManager/index.ts
new file mode 100644
index 0000000..65d4de9
--- /dev/null
+++ b/src/ts/common/componentManager/index.ts
@@ -0,0 +1 @@
+export * from "./componentManager"
\ No newline at end of file
diff --git a/src/ts/common/componentManager/interfaces.ts b/src/ts/common/componentManager/interfaces.ts
new file mode 100644
index 0000000..54b1bc5
--- /dev/null
+++ b/src/ts/common/componentManager/interfaces.ts
@@ -0,0 +1,5 @@
+import { ComponentState } from "../component/interfaces";
+
+export interface MnanagerState {
+    components: ComponentState[]
+}
\ No newline at end of file
diff --git a/src/ts/common/screen.ts/index.ts b/src/ts/common/screen.ts/index.ts
new file mode 100644
index 0000000..4cc2a70
--- /dev/null
+++ b/src/ts/common/screen.ts/index.ts
@@ -0,0 +1 @@
+export * from "./screen"
\ No newline at end of file
diff --git a/src/ts/common/screen.ts/screen.ts b/src/ts/common/screen.ts/screen.ts
new file mode 100644
index 0000000..a2ca841
--- /dev/null
+++ b/src/ts/common/screen.ts/screen.ts
@@ -0,0 +1,72 @@
+import { Subject, fromEvent, BehaviorSubject, combineLatest } from "rxjs"
+import { Singleton } from "@eix/utils"
+import { map } from "rxjs/operators";
+import clamp from "../clamp/clamp";
+
+@Singleton
+export class Screen {
+    width = new BehaviorSubject<number>(0)
+    height = new BehaviorSubject<number>(0)
+    viewBox = combineLatest(this.width, this.height).pipe(map((values: [number,number]) =>
+        this.getViewBox(...values)
+    ));
+
+    private scrollStep = 1.3
+    private position = [0, 0]
+    public scale = [2, 2]
+
+    private zoomLimits: [number,number] = [0.1,10]
+
+    public mousePosition = [this.width.value / 2, this.height.value / 2]
+
+    constructor() {
+        this.update()
+
+        fromEvent(window, "resize").subscribe(() => this.update())
+    }
+
+    updateMouse(e:MouseEvent){
+        this.mousePosition = [e.clientX,e.clientY]
+    }
+
+    handleScroll(e: WheelEvent){
+        e.preventDefault()
+
+        const size = [this.width.value,this.height.value]
+        const mouseFraction = size.map((value,index) => this.mousePosition[index] / value)
+        const sign = e.deltaY / Math.abs(e.deltaY)
+        const zoom = this.scrollStep ** sign
+        const newScale = this.scale.map(value => clamp(value * zoom, ...this.zoomLimits ))
+        const delta = this.scale.map((value,index) => 
+            size[index] * (newScale[index] - this.scale[index]) * mouseFraction[index]
+        )
+
+        this.scale = newScale
+        this.position = this.position.map((value,index) => value - delta[index])
+        this.update()
+    }
+
+    move(x:number, y:number) {
+        this.position[0] += x * this.scale[0]
+        this.position[1] += y * this.scale[1]
+        this.update()
+    }
+
+    getViewBox(width: number, height:number) {
+        return [
+            this.position[0],
+            this.position[1],
+            this.scale[0] * width,
+            this.scale[1] * height
+        ].join(" ")
+    }
+
+    update() {
+        this.height.next(window.innerHeight)
+        this.width.next(window.innerWidth)
+    }
+
+    getWorldPosition(x:number, y:number) {
+        return [x * this.scale[0], y * this.scale[1]]
+    }
+}
\ No newline at end of file
diff --git a/src/ts/main.ts b/src/ts/main.ts
index 8c1ba46..9e7449f 100644
--- a/src/ts/main.ts
+++ b/src/ts/main.ts
@@ -1 +1,40 @@
-console.log("hello world!")
\ No newline at end of file
+import { render, html, svg } from "lit-html"
+import { subscribe } from "lit-rx"
+import { Screen } from "./common/screen.ts";
+import { Component } from "./common/component";
+import { FunctionStore } from "./common/activation/activationStore";
+import { ComponentManager } from "./common/componentManager";
+
+const screen = new Screen()
+
+const test = new FunctionStore()
+test.register("buffer",(data) => {
+    return true;
+})
+
+const manager = new ComponentManager()
+manager.components.push(new Component("none",[200,100],[200,30]))
+manager.components.push(new Component("none",[300,100],[200,30]))
+manager.components.push(new Component("none",[400,100],[200,30]))
+manager.components.push(new Component("none",[500,100],[200,30]))
+manager.components.push(new Component("none",[600,100],[200,30]))
+manager.update()
+
+console.log(manager.state)
+
+render(html`
+    <svg height=${ subscribe(screen.height) } width=${ subscribe(screen.width) }
+        @mousemove=${(e:MouseEvent) => {
+            manager.handleMouseMove(e)
+            screen.updateMouse(e)
+        }}
+        viewBox=${subscribe(screen.viewBox)}
+        @mousedown=${(e:MouseEvent) => manager.handleMouseDown(e)}
+        @mouseup=${(e:MouseEvent) => manager.handleMouseUp(e)}
+        @wheel=${(e:WheelEvent) => screen.handleScroll(e)}
+    >
+        ${ subscribe(manager.svgs) }
+    </svg>
+`, document.body)
+
+manager.update()
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 2730819..cac08af 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,7 @@
                 "types/*"
             ]
         },
-        "module": "es6",
+        "module": "commonjs",
         "target": "es5",
         "removeComments": true,
         "sourceMap": true,
@@ -17,7 +17,9 @@
         ],
         "noImplicitAny": true,
         "alwaysStrict": true,
-        "moduleResolution": "Node"
+        "moduleResolution": "Node",
+        "experimentalDecorators": true,
+        "esModuleInterop": true
     },
     "include": [
         "src/**/*.ts"