diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a60a986
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+/__pycache__
+/cards/*
+/words/*
+!/cards/.placeholder
+!/words/.placeholder
+!/words/words.txt.example
+config.py
diff --git a/cards/.placeholder b/cards/.placeholder
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cards/.placeholder
diff --git a/config.py.example b/config.py.example
new file mode 100644
index 0000000..212f890
--- /dev/null
+++ b/config.py.example
@@ -0,0 +1,9 @@
+
+hostName = "127.0.0.1"
+serverPort = 8080
+externalHost = "http://" + hostName + ":" + str(serverPort)
+prefix = "/pixit"
+cardsPrefix = prefix + "/cards"
+
+cardsCount = 7
+
diff --git a/game.py b/game.py
new file mode 100644
index 0000000..e33f968
--- /dev/null
+++ b/game.py
@@ -0,0 +1,136 @@
+
+import config
+
+import os
+import uuid
+import random
+
+
+class Player:
+    def __init__(self, game, name):
+        self.game = game
+        self.name = name
+        self.id = str(uuid.uuid4())
+        self.beginturn()
+        players[self.id] = self
+
+    def beginturn(self):
+        self.word = ""
+        self.ready = False
+        self.selection = -1
+
+    def select(self, i):
+        if self.selection < 0 and i >= 0 and i < len(self.game.cards):
+            self.selection = i
+            self.game.playerSelect()
+
+    def nextturn(self, word):
+        if self.game.selected and not self.ready:
+            self.word = str(word).strip()
+            self.ready = True
+            self.game.playerReady()
+
+    def status(self):
+        friend = self.game.players[1] if self.game.players[0] == self else self.game.players[0]
+        friendId = friend.id if self.game.players[0] == self else None
+        friendSelection = friend.selection if self.game.selected else -1
+        cards = []
+        for i in range(0, len(self.game.cards)):
+            cards.append({ "index": i, "name": self.game.cards[i], "my": self.selection == i, "friends": friendSelection == i })
+        return {
+            "turn": self.game.turn,
+            "score": self.game.score,
+            "word": self.game.word,
+            "wordSource": self.game.wordSource,
+            "cards": cards,
+            "selected": self.game.selected,
+            "host": config.externalHost,
+            "prefix": config.prefix,
+            "cardsPrefix": config.cardsPrefix,
+            "waiting": (self.selection >= 0 and friendSelection < 0) or (self.ready and not friend.ready),
+            "win": self.game.selected and self.selection == friend.selection,
+            "lose": self.game.selected and self.selection != friend.selection,
+            "me": {
+                "id": self.id,
+                "name": self.name,
+                "selection": self.selection,
+                "selected": self.selection >= 0,
+                "ready": self.ready },
+            "friend": {
+                "id": friendId,
+                "selection": friendSelection } }
+
+
+class Game:
+    def __init__(self):
+        self.players = ( Player(self, "Player1"), Player(self, "Player2") )
+        self.turn = 1
+        self.score = 0
+        self.cards = []
+        self.beginturn()
+
+    def beginturn(self):
+        assert(cards)
+        self.cards.clear()
+        for i in range(0, config.cardsCount):
+            for j in range(0, 100):
+                card = cards[ random.randrange(0, len(cards)) ]
+                if not card in self.cards:
+                    self.cards.append(card)
+                    break
+        w0 = self.players[0].word
+        w1 = self.players[1].word
+        if not w0 and not w1:
+            self.word = random.choice(words)
+            self.wordSource = "choosen by random"
+        elif not w1:
+            self.word = w0
+            self.wordSource = "suggested by player1"
+        elif not w0:
+            self.word = w1
+            self.wordSource = "suggested by player2"
+        elif w0 == w1:
+            self.word = w0
+            self.wordSource = "suggested by both players"
+        elif random.randrange(0, 1):
+            self.word = w0
+            self.wordSource = "suggested by player1 (player2 may be lucky next time)"
+        else:
+            self.word = w1
+            self.wordSource = "suggested by player2 (player1 may be lucky next time)"
+        self.players[0].beginturn()
+        self.players[1].beginturn()
+        self.selected = False
+
+    def playerSelect(self):
+        if not self.selected and self.players[0].selection >= 0 and self.players[1].selection >= 0:
+            self.selected = True
+            if self.players[0].selection == self.players[1].selection:
+                self.score = self.score + 1
+
+    def playerReady(self):
+        if self.selected and self.players[0].ready and self.players[1].ready:
+            self.turn = self.turn + 1
+            self.beginturn()
+
+
+def mergeCards(path):
+    for f in os.scandir(path):
+        if f.is_file() and f.name.endswith(".jpg"):
+            cards.append(f.name)
+
+def mergeWords(path):
+    global words
+    ws = set(words)
+    with open(path, "r") as f:
+        for word in f.readlines():
+            w = word.strip()
+            if w != "":
+                ws.add(w)
+    words.clear()
+    words += ws
+
+players = {}
+cards = []
+words = []
+
diff --git a/server.py b/server.py
new file mode 100755
index 0000000..b6283c7
--- /dev/null
+++ b/server.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python3
+
+import config
+import game
+import template
+
+import os
+import json
+import threading
+import http.server
+import urllib.parse
+
+
+class Server(http.server.BaseHTTPRequestHandler):
+    def write(self, *texts):
+        for text in texts:
+            self.wfile.write(bytes(str(text), "utf8"))
+
+    def parsePath(self, path):
+        if not path.startswith(config.prefix):
+            return None
+        path = path[len(config.prefix):]
+        if len(path) and path[0] != '/':
+            return None
+        return list(filter(None, path.split("/")))
+
+    def err(self, code = 404, message = ""):
+        self.send_response(int(code))
+        self.send_header("Content-type", "text/html")
+        self.end_headers()
+        self.write(str(code) + " " + str(message));
+
+    def writeJpeg(self, path):
+        if not path.endswith(".jpg") or not os.path.isfile(path):
+            return self.err()
+        with open(path, "rb") as f:
+            self.send_response(200)
+            self.send_header("Content-type", "image/jpeg")
+            self.send_header("Cache-Control", "max-age=3600")
+            self.end_headers()
+            self.wfile.write(f.read())
+
+    def writeStatus(self, player):
+        self.send_response(200)
+        self.send_header("Content-type", "text/html")
+        self.end_headers()
+        self.write(json.dumps(player.status()))
+
+    def do_POST(self):
+        if self.command != 'POST':
+            return self.err()
+        path = self.parsePath(self.path)
+        if path is None:
+            return self.err()
+
+        length = int(self.headers.get('content-length'))
+        data = self.rfile.read(length)
+        fields = urllib.parse.parse_qs(str(data,"UTF-8"))
+
+        with mutex:
+            if not path: # create new game
+                g = game.Game()
+                self.send_response(303)
+                self.send_header("Location", str(config.prefix) + "/" + str(g.players[0].id))
+                self.end_headers()
+                return
+
+            player = game.players.get(path[0], None)
+            if not player:
+                return self.err()
+            del path[0]
+
+            if len(path) == 2 and path[0] == "select":
+                i = -1
+                try:
+                    i = int(path[1])
+                except ValueError:
+                    return self.err()
+                player.select(i)
+            elif len(path) == 1 and path[0] == "ready":
+                word = str(fields.get("word", [""])[0])
+                player.nextturn(word)
+            else:
+                return self.err()
+
+            self.send_response(303)
+            self.send_header("Location", str(config.prefix) + "/" + str(player.id))
+            self.end_headers()
+
+    def do_GET(self):
+        if self.command != 'GET':
+            return self.err()
+
+        if self.path.startswith(config.cardsPrefix):
+            p = str(self.path[len(config.cardsPrefix):])
+            if p and p[0] == "/":
+                if p.count("/") > 1 or p.count("..") > 0:
+                    return self.err()
+                return self.writeJpeg("cards" + p)
+
+        path = self.parsePath(self.path)
+        if path is None:
+            return self.err()
+
+        if not path:
+            self.send_response(200)
+            self.send_header("Content-type", "text/html")
+            self.end_headers()
+            tplStartpage.write(self, {"host": config.externalHost, "prefix": config.prefix});
+            return
+
+        with mutex:
+            if len(path) != 1:
+                return self.err()
+            j = False
+            if path[0].endswith(".json"):
+                j = True
+                path[0] = path[0][0:-5]
+            player = game.players.get(path[0], None)
+            if not player:
+                return self.err()
+            if j:
+                return self.writeStatus(player)
+            self.send_response(200)
+            self.send_header("Content-type", "text/html")
+            self.end_headers()
+            tplPlayerpage = template.TplLoader.load("tpl/playerpage.tpl")
+            tplPlayerpage.write(self, player.status());
+
+
+mutex = threading.Lock()
+
+tplStartpage = template.TplLoader.load("tpl/startpage.tpl")
+tplPlayerpage = template.TplLoader.load("tpl/playerpage.tpl")
+
+game.mergeCards("cards")
+game.mergeWords("words/words.txt")
+
+webServer = http.server.HTTPServer((
+    config.hostName,
+    config.serverPort ), Server )
+
+
+print("Server started http://%s:%s" % (
+    config.hostName,
+    config.serverPort ))
+try:
+    webServer.serve_forever()
+except KeyboardInterrupt:
+    pass
+webServer.server_close()
+print("Server stopped.")
diff --git a/template.py b/template.py
new file mode 100644
index 0000000..49b38ea
--- /dev/null
+++ b/template.py
@@ -0,0 +1,417 @@
+
+import os
+import html
+import markdown
+os.path.dirname(os.path.abspath(__file__))
+
+
+def dictGet(d, path):
+    for k in str(path).split('.'):
+        if type(d) is list or type(d) is tuple:
+            d = d[int(k)]
+            continue
+        elif type(d) is dict:
+            if (not k in d) and ('ex' in d):
+                print("query ex for field: %s (%s)" % (path, k))
+                d = d['ex'](d)
+            if k in d:
+                d = d[k]
+                continue
+        print("variable not found: %s (%s)" % (path, k))
+        return None
+    return d
+
+def tostr(s):
+    return '' if s is None else str(s) 
+def text2html(t):
+    return html.escape(tostr(t))
+def md2html(t):
+    return markdown.markdown(tostr(t))
+
+
+tabsym = ' -- '
+
+
+class TemplateText:
+    def __init__(self, text = ''):
+        self.text = text
+    def write(self, writer, context):
+        writer.write(self.text)
+    def optimize(self):
+        return self if self.text else None
+    def dbgPrint(self, tab):
+        print("%stext(%s)" % (tab, self.text.replace("\n", "\\n")))
+
+class TemplateVarHtml:
+    def __init__(self, varpath = None):
+        self.varpath = varpath
+    def write(self, writer, context):
+        writer.write(tostr(dictGet( context, self.varpath )))
+    def optimize(self):
+        return self
+    def dbgPrint(self, tab):
+        print("%svarHtml(%s)" % (tab, self.varpath))
+
+class TemplateVarMd:
+    def __init__(self, varpath = None):
+        self.varpath = varpath
+    def write(self, writer, context):
+        writer.write(md2html(dictGet( context, self.varpath )))
+    def optimize(self):
+        return self
+    def dbgPrint(self, tab):
+        print("%svarMd(%s)" % (tab, self.varpath))
+
+class TemplateVarText:
+    def __init__(self, varpath = None):
+        self.varpath = varpath
+    def write(self, writer, context):
+        writer.write(text2html(dictGet( context, self.varpath )))
+    def optimize(self):
+        return self
+    def dbgPrint(self, tab):
+        print("%svarText(%s)" % (tab, self.varpath))
+
+class TemplateVarCount:
+    def __init__(self, varpath = None):
+        self.varpath = varpath
+    def write(self, writer, context):
+        v = dictGet( context, self.varpath )
+        writer.write(str(len(v) if hasattr(v, '__len__') else 0))
+    def optimize(self):
+        return self
+    def dbgPrint(self, tab):
+        print("%svarCount(%s)" % (tab, self.varpath))
+
+class TemplateVarField:
+    def __init__(self, varpath = None):
+        self.varpath = varpath
+    def write(self, writer, context):
+        f = self.varpath.split('.')[-1]
+        writer.write("<p><b>%s:</b> %s</p>" % (
+            text2html( f ),
+            text2html( dictGet(context, self.varpath) ) ))
+    def optimize(self):
+        return self
+    def dbgPrint(self, tab):
+        print("%svarField(%s)" % (tab, self.varpath))
+
+class TemplateIf:
+    def __init__(self, varpath = None, sub = None, alt = None):
+        self.varpath = varpath
+        self.sub = sub
+        self.alt = alt
+    def write(self, writer, context):
+        if dictGet(context, self.varpath):
+            if self.sub: self.sub.write(writer, context)
+        else:
+            if self.alt: self.alt.write(writer, context)
+    def optimize(self):
+        self.sub = self.sub.optimize() if self.sub else None
+        self.alt = self.alt.optimize() if self.alt else None
+        return self if self.sub or self.alt else None
+    def dbgPrint(self, tab):
+        print("%sif(%s):" % (tab, self.varpath))
+        if self.sub:
+            self.sub.dbgPrint(tab + tabsym)
+        if self.alt:
+            print("%selse:" % tab)
+            self.alt.dbgPrint(tab + tabsym)
+
+
+class TemplateWith:
+    def __init__(self, varpath = None, varname = None, sub = None):
+        self.varpath = varpath
+        self.varname = varname
+        self.sub = sub
+    def write(self, writer, context):
+        if self.sub:
+            context = dict(context)
+            context[self.varname] = dictGet(context, self.varpath)
+            self.sub.write(writer, context)
+    def optimize(self):
+        self.sub = self.sub.optimize() if self.sub else None
+        return self if self.sub else None
+    def dbgPrint(self, tab):
+        print("%swith(%s:%s):" % (tab, self.varpath, self.varname))
+        if self.sub: self.sub.dbgPrint(tab + tabsym)
+
+
+class TemplateComment:
+    def __init__(self, text = None):
+        self.text = text
+    def write(self, writer, context):
+        pass
+    def optimize(self):
+        return None
+    def dbgPrint(self, tab):
+        print("%scomment(%s)" % (texttab, self.text))
+
+
+class TemplateLoop:
+    def __init__(self, varpath = None, keyvarname = None, varname = None, sub = None, sep = None):
+        self.varpath = varpath
+        self.keyvarname = keyvarname
+        self.varname = varname
+        self.sub = sub
+        self.sep = sep
+    def writeItem(self, writer, context, first, k, v):
+        if self.sep and not first:
+            self.sep.write(writer, context)
+        if self.sub:
+            ctx = dict(context)
+            ctx[self.keyvarname] = k
+            ctx[self.varname] = v
+            self.sub.write(writer, ctx)
+    def write(self, writer, context):
+        if self.sep or self.sub:
+            vv = dictGet(context, self.varpath)
+            if type(vv) is dict:
+                f = True
+                keys = list(vv.keys())
+                keys.sort()
+                for k in keys:
+                    self.writeItem(writer, context, f, k, vv[k])
+                    f = False
+            elif hasattr(vv, '__iter__'):
+                idx = 0
+                for v in vv:
+                    self.writeItem(writer, context, not idx, idx, v)
+                    idx = idx + 1
+    def optimize(self):
+        self.sub = self.sub.optimize() if self.sub else None
+        self.sep = self.sep.optimize() if self.sep else None
+        return self if self.sub or self.sep else None
+    def dbgPrint(self, tab):
+        print("%sloop(%s:%s:%s):" % (tab, self.varpath, self.keyvarname, self.varname))
+        if self.sub:
+            self.sub.dbgPrint(tab + tabsym)
+        if self.sep:
+            print("%ssep:" % tab)
+            self.sep.dbgPrint(tab + tabsym)
+
+class Template:
+    def __init__(self):
+        self.items = []
+    def write(self, writer, context):
+        for i in self.items:
+            i.write(writer, context)
+    def optimize(self):
+        it = []
+        for i in self.items:
+            ii = i.optimize()
+            if ii:
+                if it and type(ii) is TemplateText and type(it[-1]) is TemplateText:
+                    it[-1].text = it[-1].text + ii.text
+                else:
+                    it.append(ii)
+        self.items = it
+        if len(self.items) == 1: return self.items[0]
+        return self if self.items else None
+    def dbgPrint(self, tab):
+        print("%stemplate:" % tab)
+        for i in self.items:
+            i.dbgPrint(tab + tabsym)
+
+
+class TplLoader:
+    @staticmethod
+    def loadtext(filename):
+        filename = os.path.abspath(filename)
+        path = os.path.dirname(filename)
+        res = ''
+        text = ''
+        with open(filename, 'r') as f: text = f.read()
+        ii = 0
+        i = 0
+        while i < len(text):
+            if text[i:i+9] == '{include:':
+                res = res + text[ii:i]
+                a = i+9
+                b = text.find('}', a)
+                if b<0: raise Exception("include parse error: %s:%d" % (filename, a))
+                fn = text[a:b]
+                res = res + TplLoader.loadtext(path +'/' + fn)
+                i = b+1
+                ii = i
+            else:
+                i = i + 1
+        return res + text[ii:i]
+
+    def __init__(self, filename):
+        self.filename = os.path.abspath(filename)
+        self.text = TplLoader.loadtext(self.filename)
+        self.ptr = 0
+
+    def error(self):
+        with open('tplerr.txt', 'w') as f: f.write(self.text)
+        raise Exception("parse error: %s:%d" % (self.filename, self.ptr))
+
+    def readKey(self, k):
+        if self.text[self.ptr:self.ptr + len(k)] == k:
+            self.ptr = self.ptr + len(k)
+            return True
+        return False
+
+    def readVarname(self):
+        i = self.ptr
+        while i < len(self.text) and (self.text[i].isalnum() or self.text[i] in '_'):
+            i = i + 1
+        if i == self.ptr: return None
+        i, self.ptr = self.ptr, i
+        return self.text[i:self.ptr]
+
+    def readVarpath(self):
+        i = self.ptr
+        varpath = None
+        while True:
+            varname = self.readVarname()
+            if not varname:
+                if varpath: self.error()
+                break
+            varpath = varpath + '.' + varname if varpath else varname
+            if not self.readKey('.'): break
+        if not varpath:
+            self.ptr = i
+        return varpath
+
+    def loadVarText(self):
+        if not self.readKey("{:"): return None
+        varpath = self.readVarpath()
+        if not varpath or not self.readKey("}"): self.error()
+        return TemplateVarText(varpath)
+
+    def loadVarHtml(self):
+        if not self.readKey("{html:"): return None
+        varpath = self.readVarpath()
+        if not varpath or not self.readKey("}"): self.error()
+        return TemplateVarHtml(varpath)
+
+    def loadVarMd(self):
+        if not self.readKey("{md:"): return None
+        varpath = self.readVarpath()
+        if not varpath or not self.readKey("}"): self.error()
+        return TemplateVarMd(varpath)
+
+    def loadVarCount(self):
+        if not self.readKey("{count:"): return None
+        varpath = self.readVarpath()
+        if not varpath or not self.readKey("}"): self.error()
+        return TemplateVarCount(varpath)
+
+    def loadVarField(self):
+        if not self.readKey("{f:"): return None
+        varpath = self.readVarpath()
+        if not varpath or not self.readKey("}"): self.error()
+        return TemplateVarField(varpath)
+
+    def loadIf(self):
+        if not self.readKey("{if:"): return None
+        varpath = self.readVarpath()
+        if not varpath or not self.readKey("}"): self.error()
+        res = TemplateIf(varpath, Template(), Template())
+        e = False
+        while True:
+            if self.readKey("{endif}"): break
+            if self.readKey("{else}"):
+                if e: self.error()
+                e = True
+                continue
+            item = self.loadTemplate()
+            if not item: self.error()
+            if e:
+                res.alt.items.append(item)
+            else:
+                res.sub.items.append(item)
+        return res
+
+    def loadWith(self):
+        if not self.readKey("{with:"): return None
+        varpath = self.readVarpath()
+        if not varpath or not self.readKey(":"): self.error()
+        varname = self.readVarname()
+        if not varname or not self.readKey("}"): self.error()
+        res = TemplateWith(varpath, varname, Template())
+        while True:
+            if self.readKey("{endwith}"): break
+            item = self.loadTemplate()
+            if not item: self.error()
+            res.sub.items.append(item)
+        return res
+
+    def loadComment(self):
+        if not self.readKey("{comment}"): return None
+        res = TemplateComment()
+        i = self.text.find('{endcomment}', self.ptr)
+        if i < 0:
+            i = len(self.text)
+        res = TemplateComment(self.text[self.ptr:i])
+        self.ptr = i
+        if not self.readKey("{endcomment}"): self.error()
+        return res
+
+    def loadLoop(self):
+        if not self.readKey("{loop:"): return None
+        varpath = self.readVarpath()
+        if not varpath or not self.readKey(":"): self.error()
+        keyvarname = self.readVarname()
+        if not self.readKey(":"): self.error()
+        varname = self.readVarname()
+        if not varname or not self.readKey("}"): self.error()
+        res = TemplateLoop(varpath, keyvarname, varname, Template(), Template())
+        s = False
+        while True:
+            if self.readKey("{endloop}"): break
+            if self.readKey("{sep}"):
+                if s: self.error()
+                s = True
+                continue
+            item = self.loadTemplate()
+            if not item: self.error()
+            if s:
+                res.sep.items.append(item)
+            else:
+                res.sub.items.append(item)
+        return res
+
+    def loadTemplate(self):
+        t = self.loadVarText()
+        if t: return t
+        t = self.loadVarHtml()
+        if t: return t
+        t = self.loadVarMd()
+        if t: return t
+        t = self.loadVarCount()
+        if t: return t
+        t = self.loadVarField()
+        if t: return t
+        t = self.loadIf()
+        if t: return t
+        t = self.loadWith()
+        if t: return t
+        t = self.loadComment()
+        if t: return t
+        t = self.loadLoop()
+        if t: return t
+
+        i = self.text.find('{', self.ptr)
+        if i == self.ptr:
+            i = self.text.find('{', self.ptr+1)
+        if i < 0:
+            i = len(self.text)
+        if i <= self.ptr:
+            return None
+        i, self.ptr = self.ptr, i
+        return TemplateText(self.text[i:self.ptr])
+
+    @staticmethod
+    def load(filename):
+        l = TplLoader(filename)
+        t = Template()
+        while True:
+            tt = l.loadTemplate()
+            if not tt: break
+            t.items.append(tt)
+        if l.ptr < len(l.text): l.error()
+        t = t.optimize()
+        return t if t else TemplateText()
diff --git a/tpl/css.tpl b/tpl/css.tpl
new file mode 100644
index 0000000..6a1aa9c
--- /dev/null
+++ b/tpl/css.tpl
@@ -0,0 +1,65 @@
+
+body { font-size: 12px; text-align: center; }
+
+#logo {
+    font-size: 16px;
+    margin: 16px;
+}
+
+#info {
+    font-size: 16px;
+    font-weight: bold;
+    margin-top: 18px;
+}
+
+#word { }
+#word h1 { font-size: 24px; font-weight: bold; margin: 18px 0 0 0; }
+
+#win {
+    font-size: 24px;
+    font-weight: bold;
+    margin: 18px;
+}
+
+#lose {
+    font-size: 24px;
+    font-weight: bold;
+    margin: 18px;
+    color: gray;
+}
+
+
+#cards {
+    margin-bottom: 18px;
+}
+
+.card {
+    display: inline-block;
+    border: 1px solid #aaaaaa;
+    border-radius: 16px;
+    margin: 8px;
+    padding: 8px;
+    width: 376px;
+}
+
+.myCard {
+    background: #aaaaff;
+}
+
+.friendsCard {
+    background: #ffaaaa;
+}
+
+.myCard.friendsCard {
+    background: #aaffaa;
+}
+
+
+.card img {
+    width: 360px;
+    height: auto;
+}
+
+#debug { display: none; }
+#ready { }
+#friendurl { }
diff --git a/tpl/footer.tpl b/tpl/footer.tpl
new file mode 100644
index 0000000..a1ed2f5
--- /dev/null
+++ b/tpl/footer.tpl
@@ -0,0 +1 @@
+{if:waiting}<script>refreshTimeout = setTimeout(function() { window.location = window.location.href; }, 5000);</script>{endif}</body></html>
diff --git a/tpl/header.tpl b/tpl/header.tpl
new file mode 100644
index 0000000..2d1c16a
--- /dev/null
+++ b/tpl/header.tpl
@@ -0,0 +1,6 @@
+<html><head>
+    <title>dixit</title>
+    <meta charset="UTF-8" />
+    <style>{include:css.tpl}</style>
+    {if:waiting}<noscript><meta http-equiv="refresh" content="5"></noscript>{endif}
+</head><body>
diff --git a/tpl/playerpage.tpl b/tpl/playerpage.tpl
new file mode 100644
index 0000000..e32f0fc
--- /dev/null
+++ b/tpl/playerpage.tpl
@@ -0,0 +1,37 @@
+{include:header.tpl}
+{if:friend.id}<div id="friendurl">URL for the friend: <a href="{:host}{:prefix}/{:friend.id}">{:host}{:prefix}/{:friend.id}</a></div>{endif}
+<div id="info">{:me.name}, score: <span id="score">{:score}</span>, turn: <span id="turn">{:turn}</span></div>
+<div id="word">
+<h1>{:word}</h1>
+<small>({:wordSource})</small>
+</div>
+
+{if:win}
+<div id="win">Answers are equal. You win!</div>
+{endif}
+{if:lose}
+<div id="lose">Answers are different. You lose...</div>
+{endif}
+
+{if:waiting}
+    <div class="waiting">waiting for the friend</div>
+{else}{if:selected}
+    <form id="ready" method="POST" action="{:prefix}/{:me.id}/ready">
+    <input type="text" name="word" autocomplete="off" placeholder="here you may suggest your word for the next turn" size=40 value="" />
+    <input type="submit" name="submit" value="next turn" />
+    </form>
+{endif}{endif}
+
+<div id="debug">my selection: {:me.selection}, friend's selection: {:friend.selection}</div>
+
+<div id="cards">{loop:cards::card}
+<form class="card{if:card.my} myCard{endif}{if:card.friends} friendsCard{endif}" method="POST" action="{:prefix}/{:me.id}/select/{:card.index}">
+<img src="{:cardsPrefix}/{:card.name}" />
+{if:me.selected}{else}<br /><input type="submit" value="select" />{endif}
+</form>
+{endloop}</div>
+
+<hr />
+<div id="logo"><a href="{:prefix}">back to start page</a></div>
+
+{include:footer.tpl}
diff --git a/tpl/startpage.tpl b/tpl/startpage.tpl
new file mode 100644
index 0000000..db595c1
--- /dev/null
+++ b/tpl/startpage.tpl
@@ -0,0 +1,5 @@
+{include:header.tpl}
+<form method="POST">
+<input type="submit" value="create game" />
+</form>
+{include:footer.tpl}
diff --git a/words/.placeholder b/words/.placeholder
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/words/.placeholder
diff --git a/words/words.txt.example b/words/words.txt.example
new file mode 100644
index 0000000..47c3851
--- /dev/null
+++ b/words/words.txt.example
@@ -0,0 +1,4 @@
+hello
+world
+good day
+bye bye