diff --git a/config/index.js b/config/index.js
index 6652048c..c48d91b8 100644
--- a/config/index.js
+++ b/config/index.js
@@ -23,9 +23,15 @@ module.exports = {
     assetsPublicPath: '/',
     proxyTable: {
       '/api': {
-        target: 'https://social.heldscal.la/',
+        target: 'htts://localhost:4000/',
         changeOrigin: true,
         cookieDomainRewrite: 'localhost'
+      },
+      '/socket': {
+        target: 'htts://localhost:4000/',
+        changeOrigin: true,
+        cookieDomainRewrite: 'localhost',
+        ws: true
       }
     },
     // CSS Sourcemaps off by default because relative paths are "buggy"
diff --git a/package.json b/package.json
index e8d84274..4e98647b 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
     "localforage": "^1.5.0",
     "node-sass": "^3.10.1",
     "object-path": "^0.11.3",
+    "phoenix": "^1.3.0",
     "sanitize-html": "^1.13.0",
     "sass-loader": "^4.0.2",
     "vue": "^2.3.4",
diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js
new file mode 100644
index 00000000..ef326d4a
--- /dev/null
+++ b/src/components/chat/chat.js
@@ -0,0 +1,21 @@
+const chat = {
+  data () {
+    return {
+      currentMessage: '',
+      channel: null
+    }
+  },
+  computed: {
+    messages () {
+      return this.$store.state.chat.messages
+    }
+  },
+  methods: {
+    submit (message) {
+      this.$store.state.chat.channel.push('new_msg', {text: message}, 10000)
+      this.currentMessage = ''
+    }
+  }
+}
+
+export default chat
diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue
new file mode 100644
index 00000000..6c1e2c38
--- /dev/null
+++ b/src/components/chat/chat.vue
@@ -0,0 +1,59 @@
+<template>
+  <div class="chat-panel panel panel-default">
+    <div class="panel-heading timeline-heading base02-background base04">
+      <div class="title">
+        {{$t('chat.title')}}
+      </div>
+    </div>
+    <div class="panel-body base01-background">
+      <div class="chat-window">
+        <div class="chat-message" v-for="message in messages" :key="message.id">
+          <span class="chat-avatar">
+            <img :src="message.author.avatar" />
+            {{message.author.username}}:
+          </span>
+          <span class="chat-text">
+            {{message.text}}
+          </span>
+        </div>
+      </div>
+      <div class="chat-input">
+        <form @submit.prevent="submit(currentMessage)">
+          <input v-model="currentMessage" type="text" >
+        </form>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script src="./chat.js"></script>
+
+
+<style lang="scss">
+ .chat-window {
+   max-height: 80vh;
+   overflow-y: auto;
+   overflow-x: hidden;
+ }
+ .chat-message {
+   padding: 0.2em 0.5em
+ }
+ .chat-avatar {
+     img {
+         height: 32px;
+         width: 32px;
+         border-radius: 5px;
+         margin-right: 0.5em;
+     }
+ }
+ .chat-input {
+     display: flex;
+     form {
+         flex: auto;
+         input {
+             margin: 0.5em;
+             width: fill-available;
+         }
+     }
+ }
+</style>
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index baeaaede..ea5d7ea4 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -2,6 +2,9 @@ const NavPanel = {
   computed: {
     currentUser () {
       return this.$store.state.users.currentUser
+    },
+    chat () {
+      return this.$store.state.chat.channel
     }
   }
 }
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
index aea841e9..ccc772a8 100644
--- a/src/components/nav_panel/nav_panel.vue
+++ b/src/components/nav_panel/nav_panel.vue
@@ -7,6 +7,11 @@
             {{ $t("nav.timeline") }}
           </router-link>
         </li>
+        <li v-if='chat && currentUser'>
+          <router-link class="base00-background" to='/chat'>
+            {{ $t("nav.chat") }}
+          </router-link>
+        </li>
         <li v-if='currentUser'>
           <router-link class="base00-background" :to="{ name: 'mentions', params: { username: currentUser.screen_name } }">
             {{ $t("nav.mentions") }}
diff --git a/src/i18n/messages.js b/src/i18n/messages.js
index 9aeffdfa..ecb3557c 100644
--- a/src/i18n/messages.js
+++ b/src/i18n/messages.js
@@ -179,7 +179,11 @@ const fi = {
 }
 
 const en = {
+  chat: {
+    title: 'Chat'
+  },
   nav: {
+    chat: 'Local Chat',
     timeline: 'Timeline',
     mentions: 'Mentions',
     public_tl: 'Public Timeline',
diff --git a/src/main.js b/src/main.js
index 6c5bf83e..a8ee5fda 100644
--- a/src/main.js
+++ b/src/main.js
@@ -12,11 +12,13 @@ import UserProfile from './components/user_profile/user_profile.vue'
 import Settings from './components/settings/settings.vue'
 import Registration from './components/registration/registration.vue'
 import UserSettings from './components/user_settings/user_settings.vue'
+import Chat from './components/chat/chat.vue'
 
 import statusesModule from './modules/statuses.js'
 import usersModule from './modules/users.js'
 import apiModule from './modules/api.js'
 import configModule from './modules/config.js'
+import chatModule from './modules/chat.js'
 
 import VueTimeago from 'vue-timeago'
 import VueI18n from 'vue-i18n'
@@ -57,10 +59,12 @@ const store = new Vuex.Store({
     statuses: statusesModule,
     users: usersModule,
     api: apiModule,
-    config: configModule
+    config: configModule,
+    chat: chatModule
   },
   plugins: [createPersistedState(persistedStateOptions)],
-  strict: process.env.NODE_ENV !== 'production'
+  strict: false // Socket modifies itself, let's ignore this for now.
+  // strict: process.env.NODE_ENV !== 'production'
 })
 
 const i18n = new VueI18n({
@@ -78,6 +82,9 @@ window.fetch('/static/config.json')
     store.dispatch('setOption', { name: 'background', value: background })
     store.dispatch('setOption', { name: 'logo', value: logo })
     store.dispatch('setOption', { name: 'registrationOpen', value: registrationOpen })
+    if (data['chatDisabled']) {
+      store.dispatch('disableChat')
+    }
 
     const routes = [
       { name: 'root', path: '/', redirect: data['defaultPath'] || '/main/all' },
@@ -90,7 +97,8 @@ window.fetch('/static/config.json')
       { name: 'mentions', path: '/:username/mentions', component: Mentions },
       { name: 'settings', path: '/settings', component: Settings },
       { name: 'registration', path: '/registration', component: Registration },
-      { name: 'user-settings', path: '/user-settings', component: UserSettings }
+      { name: 'user-settings', path: '/user-settings', component: UserSettings },
+      { name: 'chat', path: '/chat', component: Chat }
     ]
 
     const router = new VueRouter({
diff --git a/src/modules/api.js b/src/modules/api.js
index e61382eb..c91fb97b 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -1,10 +1,13 @@
 import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
 import {isArray} from 'lodash'
+import { Socket } from 'phoenix'
 
 const api = {
   state: {
     backendInteractor: backendInteractorService(),
-    fetchers: {}
+    fetchers: {},
+    socket: null,
+    chatDisabled: false
   },
   mutations: {
     setBackendInteractor (state, backendInteractor) {
@@ -15,6 +18,12 @@ const api = {
     },
     removeFetcher (state, {timeline}) {
       delete state.fetchers[timeline]
+    },
+    setSocket (state, socket) {
+      state.socket = socket
+    },
+    setChatDisabled (state, value) {
+      state.chatDisabled = value
     }
   },
   actions: {
@@ -37,6 +46,17 @@ const api = {
       const fetcher = store.state.fetchers[timeline]
       window.clearInterval(fetcher)
       store.commit('removeFetcher', {timeline})
+    },
+    initializeSocket (store, token) {
+      // Set up websocket connection
+      if (!store.state.chatDisabled) {
+        let socket = new Socket('/socket', {params: {token: token}})
+        socket.connect()
+        store.dispatch('initializeChat', socket)
+      }
+    },
+    disableChat (store) {
+      store.commit('setChatDisabled', true)
     }
   }
 }
diff --git a/src/modules/chat.js b/src/modules/chat.js
new file mode 100644
index 00000000..b1244ebe
--- /dev/null
+++ b/src/modules/chat.js
@@ -0,0 +1,33 @@
+const chat = {
+  state: {
+    messages: [],
+    channel: null
+  },
+  mutations: {
+    setChannel (state, channel) {
+      state.channel = channel
+    },
+    addMessage (state, message) {
+      state.messages.push(message)
+      state.messages = state.messages.slice(-19, 20)
+    },
+    setMessages (state, messages) {
+      state.messages = messages.slice(-19, 20)
+    }
+  },
+  actions: {
+    initializeChat (store, socket) {
+      const channel = socket.channel('chat:public')
+      channel.on('new_msg', (msg) => {
+        store.commit('addMessage', msg)
+      })
+      channel.on('messages', ({messages}) => {
+        store.commit('setMessages', messages)
+      })
+      channel.join()
+      store.commit('setChannel', channel)
+    }
+  }
+}
+
+export default chat
diff --git a/src/modules/users.js b/src/modules/users.js
index 30f8dc27..8303ecc1 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -97,6 +97,10 @@ const users = {
                   // Set our new backend interactor
                   commit('setBackendInteractor', backendInteractorService(userCredentials))
 
+                  if (user.token) {
+                    store.dispatch('initializeSocket', user.token)
+                  }
+
                   // Start getting fresh tweets.
                   store.dispatch('startFetching', 'friends')
 
diff --git a/static/config.json b/static/config.json
index b186246b..880efca8 100644
--- a/static/config.json
+++ b/static/config.json
@@ -4,5 +4,6 @@
   "background": "/static/bg.jpg",
   "logo": "/static/logo.png",
   "registrationOpen": false,
-  "defaultPath": "/main/all"
+  "defaultPath": "/main/all",
+  "chatDisabled": false
 }
diff --git a/yarn.lock b/yarn.lock
index d0d2dde9..3fcd29ab 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4226,6 +4226,10 @@ phantomjs-prebuilt@^2.1.3, phantomjs-prebuilt@^2.1.7:
     request-progress "~2.0.1"
     which "~1.2.10"
 
+phoenix@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/phoenix/-/phoenix-1.3.0.tgz#1df2c27f986ee295e37c9983ec28ebac1d7f4a3e"
+
 pify@^2.0.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"