diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..01d7f95
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+__pycache__
+venv
\ No newline at end of file
diff --git a/mockup/index.html b/mockup/index.html
deleted file mode 100644
index b3acd47..0000000
--- a/mockup/index.html
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
-
-
-
-
-
- IF Game
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/mockup/starthrs.bat b/mockup/starthrs.bat
deleted file mode 100644
index 7a9716a..0000000
--- a/mockup/starthrs.bat
+++ /dev/null
@@ -1 +0,0 @@
-hrs
\ No newline at end of file
diff --git a/src/blockingSampleApp.bat b/src/blockingSampleApp.bat
new file mode 100644
index 0000000..87892a8
--- /dev/null
+++ b/src/blockingSampleApp.bat
@@ -0,0 +1,2 @@
+call ../venv/scripts/Activate.bat
+uvicorn blockingSampleApp:app --reload --no-access-log --log-level warning --port 5777 --host 0.0.0.0
\ No newline at end of file
diff --git a/src/blockingSampleApp.py b/src/blockingSampleApp.py
new file mode 100644
index 0000000..feed56d
--- /dev/null
+++ b/src/blockingSampleApp.py
@@ -0,0 +1,53 @@
+#standard
+import asyncio
+import html
+import traceback
+#custom
+import dataset
+from fastapi import FastAPI, WebSocket, Form, File, UploadFile, Response, Request, WebSocketDisconnect, websockets
+from fastapi.responses import HTMLResponse
+from fastapi.staticfiles import StaticFiles
+from fastapi.templating import Jinja2Templates
+import starlette.status as status
+from starlette.responses import RedirectResponse
+
+app = FastAPI()
+app.mount('/static', StaticFiles(directory="./static"), name="static")
+templates = Jinja2Templates(directory="templates")
+@app.get('/', response_class=HTMLResponse)
+async def getRoot(request: Request):
+ return RedirectResponse('/static/index.html', status_code=status.HTTP_302_FOUND)
+
+db = dataset.connect('sqlite:///../state.db')
+
+@app.websocket('/websocket')
+async def runGameSession(websocket: WebSocket):
+ await websocket.accept()
+ try:
+ await websocket.send_json({
+ 'type': 'header',
+ 'rawHTML': 'Testing Header Text'
+ })
+
+ while True:
+ await websocket.send_json({
+ 'type': 'narrative',
+ 'rawHTML': 'Something interesting happened.'
+ })
+ while True:
+ try:
+ resp = await asyncio.wait_for(websocket.receive_json(), timeout=5)
+ await websocket.send_json({
+ 'type': 'playerEcho',
+ 'rawHTML': '> ' + html.escape(resp['playerInput'])
+ })
+ break
+ except asyncio.exceptions.TimeoutError:
+ # timeout
+ await websocket.send_json({
+ 'type': 'narrativeUpdate',
+ 'rawHTML': 'Something EVEN MORE interesting happened.'
+ })
+ except Exception as e:
+ traceback.print_exc()
+
\ No newline at end of file
diff --git a/src/requirements.txt b/src/requirements.txt
new file mode 100644
index 0000000..eb9d006
--- /dev/null
+++ b/src/requirements.txt
@@ -0,0 +1,9 @@
+dataset
+click
+tracery
+stringcase
+fastapi
+aiofiles
+python-multipart
+uvicorn[standard]
+jinja2
\ No newline at end of file
diff --git a/mockup/static/alpine.2.8.2.min.js b/src/static/alpine.2.8.2.min.js
similarity index 100%
rename from mockup/static/alpine.2.8.2.min.js
rename to src/static/alpine.2.8.2.min.js
diff --git a/mockup/static/custom.css b/src/static/custom.css
similarity index 100%
rename from mockup/static/custom.css
rename to src/static/custom.css
diff --git a/mockup/static/htmx.min.js b/src/static/htmx.min.js
similarity index 100%
rename from mockup/static/htmx.min.js
rename to src/static/htmx.min.js
diff --git a/src/static/index.html b/src/static/index.html
new file mode 100644
index 0000000..4de1bd3
--- /dev/null
+++ b/src/static/index.html
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+ IF Game
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mockup/static/normalize.css b/src/static/normalize.css
similarity index 100%
rename from mockup/static/normalize.css
rename to src/static/normalize.css
diff --git a/src/static/reconnecting-websocket.js b/src/static/reconnecting-websocket.js
new file mode 100644
index 0000000..0054088
--- /dev/null
+++ b/src/static/reconnecting-websocket.js
@@ -0,0 +1 @@
+!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a});
\ No newline at end of file
diff --git a/src/templates/put_jinja2_templates_here.md b/src/templates/put_jinja2_templates_here.md
new file mode 100644
index 0000000..e69de29