diff --git a/docs/setup.en.md b/docs/setup.en.md
index 3e48935346..dbc0599b5a 100644
--- a/docs/setup.en.md
+++ b/docs/setup.en.md
@@ -25,6 +25,7 @@ Note that Misskey uses following subdomains:
 * **api**.*{primary domain}*
 * **auth**.*{primary domain}*
 * **about**.*{primary domain}*
+* **ch**.*{primary domain}*
 * **stats**.*{primary domain}*
 * **status**.*{primary domain}*
 * **dev**.*{primary domain}*
diff --git a/docs/setup.ja.md b/docs/setup.ja.md
index 4f48a08088..602fd9b6a1 100644
--- a/docs/setup.ja.md
+++ b/docs/setup.ja.md
@@ -26,6 +26,7 @@ Misskeyは以下のサブドメインを使います:
 * **api**.*{primary domain}*
 * **auth**.*{primary domain}*
 * **about**.*{primary domain}*
+* **ch**.*{primary domain}*
 * **stats**.*{primary domain}*
 * **status**.*{primary domain}*
 * **dev**.*{primary domain}*
diff --git a/locales/en.yml b/locales/en.yml
index 5c7a1165ba..643649b46c 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -164,6 +164,12 @@ common:
     mk-uploader:
       waiting: "Waiting"
 
+ch:
+  tags:
+    mk-index:
+      new: "Create new channel"
+      channel-title: "Channel title"
+
 desktop:
   tags:
     mk-api-info:
@@ -241,7 +247,7 @@ desktop:
     mk-ui-header-nav:
       home: "Home"
       messaging: "Messages"
-      channels: "Channels"
+      ch: "Channels"
       info: "News"
 
     mk-ui-header-search:
@@ -352,10 +358,6 @@ desktop:
     mk-repost-form-window:
       title: "Are you sure you want to repost this post?"
 
-    mk-channels-page:
-      new: "Create new channel"
-      channel-title: "Channel title"
-
 mobile:
   tags:
     mk-drive-file-viewer:
@@ -496,6 +498,7 @@ mobile:
       home: "Home"
       notifications: "Notifications"
       messaging: "Messages"
+      ch: "Channels"
       drive: "Drive"
       settings: "Settings"
       about: "About Misskey"
diff --git a/locales/ja.yml b/locales/ja.yml
index dd76a2b900..9fd7d94f0b 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -164,6 +164,12 @@ common:
     mk-uploader:
       waiting: "待機中"
 
+ch:
+  tags:
+    mk-index:
+      new: "チャンネルを作成"
+      channel-title: "チャンネルのタイトル"
+
 desktop:
   tags:
     mk-api-info:
@@ -241,7 +247,7 @@ desktop:
     mk-ui-header-nav:
       home: "ホーム"
       messaging: "メッセージ"
-      channels: "チャンネル"
+      ch: "チャンネル"
       info: "お知らせ"
 
     mk-ui-header-search:
@@ -352,10 +358,6 @@ desktop:
     mk-repost-form-window:
       title: "この投稿をRepostしますか?"
 
-    mk-channels-page:
-      new: "チャンネルを作成"
-      channel-title: "チャンネルのタイトル"
-
 mobile:
   tags:
     mk-drive-file-viewer:
@@ -496,6 +498,7 @@ mobile:
       home: "ホーム"
       notifications: "通知"
       messaging: "メッセージ"
+      ch: "チャンネル"
       search: "検索"
       drive: "ドライブ"
       settings: "設定"
diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts
index 183cabf135..34265dcbc3 100644
--- a/src/api/endpoints/posts/create.ts
+++ b/src/api/endpoints/posts/create.ts
@@ -13,7 +13,7 @@ import Watching from '../../models/post-watching';
 import serialize from '../../serializers/post';
 import notify from '../../common/notify';
 import watch from '../../common/watch-post';
-import event from '../../event';
+import { default as event, publishChannelStream } from '../../event';
 import config from '../../../conf';
 
 /**
@@ -258,6 +258,11 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => {
 	// Publish event to myself's stream
 	event(user._id, 'post', postObj);
 
+	// Publish event to channel
+	if (channel) {
+		publishChannelStream(channel._id, 'post', postObj);
+	}
+
 	// Fetch all followers
 	const followers = await Following
 		.find({
diff --git a/src/api/event.ts b/src/api/event.ts
index 9613a9f7cc..909b0d2556 100644
--- a/src/api/event.ts
+++ b/src/api/event.ts
@@ -25,6 +25,10 @@ class MisskeyEvent {
 		this.publish(`messaging-stream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
+	public publishChannelStream(channelId: ID, type: string, value?: any): void {
+		this.publish(`channel-stream:${channelId}`, type, typeof value === 'undefined' ? null : value);
+	}
+
 	private publish(channel: string, type: string, value?: any): void {
 		const message = value == null ?
 			{ type: type } :
@@ -41,3 +45,5 @@ export default ev.publishUserStream.bind(ev);
 export const publishPostStream = ev.publishPostStream.bind(ev);
 
 export const publishMessagingStream = ev.publishMessagingStream.bind(ev);
+
+export const publishChannelStream = ev.publishChannelStream.bind(ev);
diff --git a/src/api/stream/channel.ts b/src/api/stream/channel.ts
new file mode 100644
index 0000000000..d67d77cbf4
--- /dev/null
+++ b/src/api/stream/channel.ts
@@ -0,0 +1,12 @@
+import * as websocket from 'websocket';
+import * as redis from 'redis';
+
+export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient): void {
+	const channel = request.resourceURL.query.channel;
+
+	// Subscribe channel stream
+	subscriber.subscribe(`misskey:channel-stream:${channel}`);
+	subscriber.on('message', (_, data) => {
+		connection.send(data);
+	});
+}
diff --git a/src/api/streaming.ts b/src/api/streaming.ts
index db600013b9..0e512fb210 100644
--- a/src/api/streaming.ts
+++ b/src/api/streaming.ts
@@ -9,6 +9,7 @@ import isNativeToken from './common/is-native-token';
 import homeStream from './stream/home';
 import messagingStream from './stream/messaging';
 import serverStream from './stream/server';
+import channelStream from './stream/channel';
 
 module.exports = (server: http.Server) => {
 	/**
@@ -26,14 +27,6 @@ module.exports = (server: http.Server) => {
 			return;
 		}
 
-		const user = await authenticate(request.resourceURL.query.i);
-
-		if (user == null) {
-			connection.send('authentication-failed');
-			connection.close();
-			return;
-		}
-
 		// Connect to Redis
 		const subscriber = redis.createClient(
 			config.redis.port, config.redis.host);
@@ -43,6 +36,19 @@ module.exports = (server: http.Server) => {
 			subscriber.quit();
 		});
 
+		if (request.resourceURL.pathname === '/channel') {
+			channelStream(request, connection, subscriber);
+			return;
+		}
+
+		const user = await authenticate(request.resourceURL.query.i);
+
+		if (user == null) {
+			connection.send('authentication-failed');
+			connection.close();
+			return;
+		}
+
 		const channel =
 			request.resourceURL.pathname === '/' ? homeStream :
 			request.resourceURL.pathname === '/messaging' ? messagingStream :
diff --git a/src/config.ts b/src/config.ts
index 46a93f5fef..18017e9740 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -88,6 +88,7 @@ type Mixin = {
 	api_url: string;
 	auth_url: string;
 	about_url: string;
+	ch_url: stirng;
 	stats_url: string;
 	status_url: string;
 	dev_url: string;
@@ -122,6 +123,7 @@ export default function load() {
 	mixin.secondary_scheme = config.secondary_url.substr(0, config.secondary_url.indexOf('://'));
 	mixin.api_url = `${mixin.scheme}://api.${mixin.host}`;
 	mixin.auth_url = `${mixin.scheme}://auth.${mixin.host}`;
+	mixin.ch_url = `${mixin.scheme}://ch.${mixin.host}`;
 	mixin.dev_url = `${mixin.scheme}://dev.${mixin.host}`;
 	mixin.about_url = `${mixin.scheme}://about.${mixin.host}`;
 	mixin.stats_url = `${mixin.scheme}://stats.${mixin.host}`;
diff --git a/src/web/app/ch/router.js b/src/web/app/ch/router.js
new file mode 100644
index 0000000000..424158f403
--- /dev/null
+++ b/src/web/app/ch/router.js
@@ -0,0 +1,32 @@
+import * as riot from 'riot';
+const route = require('page');
+let page = null;
+
+export default me => {
+	route('/',         index);
+	route('/:channel', channel);
+	route('*',         notFound);
+
+	function index() {
+		mount(document.createElement('mk-index'));
+	}
+
+	function channel(ctx) {
+		const el = document.createElement('mk-channel');
+		el.setAttribute('id', ctx.params.channel);
+		mount(el);
+	}
+
+	function notFound() {
+		mount(document.createElement('mk-not-found'));
+	}
+
+	// EXEC
+	route();
+};
+
+function mount(content) {
+	if (page) page.unmount();
+	const body = document.getElementById('app');
+	page = riot.mount(body.appendChild(content))[0];
+}
diff --git a/src/web/app/ch/script.js b/src/web/app/ch/script.js
new file mode 100644
index 0000000000..760d405c52
--- /dev/null
+++ b/src/web/app/ch/script.js
@@ -0,0 +1,18 @@
+/**
+ * Channels
+ */
+
+// Style
+import './style.styl';
+
+require('./tags');
+import init from '../init';
+import route from './router';
+
+/**
+ * init
+ */
+init(me => {
+	// Start routing
+	route(me);
+});
diff --git a/src/web/app/ch/style.styl b/src/web/app/ch/style.styl
new file mode 100644
index 0000000000..2fc3ac3fca
--- /dev/null
+++ b/src/web/app/ch/style.styl
@@ -0,0 +1,4 @@
+@import "../base"
+
+html
+	background #efefef
diff --git a/src/web/app/desktop/tags/pages/channel.tag b/src/web/app/ch/tags/channel.tag
similarity index 76%
rename from src/web/app/desktop/tags/pages/channel.tag
rename to src/web/app/ch/tags/channel.tag
index a14c0648c4..b16844b8bc 100644
--- a/src/web/app/desktop/tags/pages/channel.tag
+++ b/src/web/app/ch/tags/channel.tag
@@ -1,14 +1,19 @@
-<mk-channel-page>
-	<mk-ui ref="ui">
-		<main if={ !parent.fetching }>
-			<h1>{ parent.channel.title }</h1>
-			<virtual if={ parent.posts }>
-				<mk-channel-post each={ parent.posts.reverse() } post={ this } form={ parent.refs.form }/>
-			</virtual>
-			<hr>
-			<mk-channel-form channel={ parent.channel } ref="form"/>
-		</main>
-	</mk-ui>
+<mk-channel>
+	<main if={ !fetching }>
+		<h1>{ channel.title }</h1>
+		<virtual if={ posts }>
+			<mk-channel-post each={ posts.slice().reverse() } post={ this } form={ parent.refs.form }/>
+		</virtual>
+		<hr>
+		<mk-channel-form if={ SIGNIN } channel={ channel } ref="form"/>
+		<div if={ !SIGNIN }>
+			<p>参加するには<a href={ CONFIG.url }>ログインまたは新規登録</a>してください</p>
+		</div>
+		<hr>
+		<footer>
+			<small>Misskey ver { version } (葵 aoi)</small>
+		</footer>
+	</main>
 	<style>
 		:scope
 			display block
@@ -20,16 +25,18 @@
 					color #f00
 	</style>
 	<script>
-		import Progress from '../../../common/scripts/loading';
-		import ChannelStream from '../../../common/scripts/channel-stream';
+		import Progress from '../../common/scripts/loading';
+		import ChannelStream from '../../common/scripts/channel-stream';
 
+		this.mixin('i');
 		this.mixin('api');
 
 		this.id = this.opts.id;
 		this.fetching = true;
 		this.channel = null;
 		this.posts = null;
-		this.connection = new ChannelStream();
+		this.connection = new ChannelStream(this.id);
+		this.version = VERSION;
 
 		this.on('mount', () => {
 			document.documentElement.style.background = '#efefef';
@@ -56,9 +63,22 @@
 					posts: posts
 				});
 			});
+
+			this.connection.on('post', this.onPost);
 		});
+
+		this.on('unmount', () => {
+			this.connection.off('post', this.onPost);
+			this.connection.close();
+		});
+
+		this.onPost = post => {
+			this.posts.unshift(post);
+			this.update();
+		};
+
 	</script>
-</mk-channel-page>
+</mk-channel>
 
 <mk-channel-post>
 	<header>
@@ -127,7 +147,7 @@
 
 	</style>
 	<script>
-		import CONFIG from '../../../common/scripts/config';
+		import CONFIG from '../../common/scripts/config';
 
 		this.mixin('api');
 
diff --git a/src/web/app/ch/tags/index.js b/src/web/app/ch/tags/index.js
new file mode 100644
index 0000000000..1e99ccd43e
--- /dev/null
+++ b/src/web/app/ch/tags/index.js
@@ -0,0 +1,2 @@
+require('./index.tag');
+require('./channel.tag');
diff --git a/src/web/app/ch/tags/index.tag b/src/web/app/ch/tags/index.tag
new file mode 100644
index 0000000000..1c0a037c2d
--- /dev/null
+++ b/src/web/app/ch/tags/index.tag
@@ -0,0 +1,24 @@
+<mk-index>
+	<button onclick={ new }>%i18n:ch.tags.mk-index.new%</button>
+	<style>
+		:scope
+			display block
+
+	</style>
+	<script>
+		this.mixin('api');
+
+		this.on('mount', () => {
+		});
+
+		this.new = () => {
+			const title = window.prompt('%i18n:ch.tags.mk-index.channel-title%');
+
+			this.api('channels/create', {
+				title: title
+			}).then(channel => {
+				location.href = '/' + channel.id;
+			});
+		};
+	</script>
+</mk-index>
diff --git a/src/web/app/common/scripts/channel-stream.js b/src/web/app/common/scripts/channel-stream.js
index 38e7d91132..17944dbe45 100644
--- a/src/web/app/common/scripts/channel-stream.js
+++ b/src/web/app/common/scripts/channel-stream.js
@@ -6,8 +6,10 @@ import Stream from './stream';
  * Channel stream connection
  */
 class Connection extends Stream {
-	constructor() {
-		super('channel');
+	constructor(channelId) {
+		super('channel', {
+			channel: channelId
+		});
 	}
 }
 
diff --git a/src/web/app/common/scripts/config.js b/src/web/app/common/scripts/config.js
index 75a7abba29..c5015622f0 100644
--- a/src/web/app/common/scripts/config.js
+++ b/src/web/app/common/scripts/config.js
@@ -6,6 +6,7 @@ const host = isRoot ? Url.host : Url.host.substring(Url.host.indexOf('.') + 1, U
 const scheme = Url.protocol;
 const url = `${scheme}//${host}`;
 const apiUrl = `${scheme}//api.${host}`;
+const chUrl = `${scheme}//ch.${host}`;
 const devUrl = `${scheme}//dev.${host}`;
 const aboutUrl = `${scheme}//about.${host}`;
 const statsUrl = `${scheme}//stats.${host}`;
@@ -16,6 +17,7 @@ export default {
 	scheme,
 	url,
 	apiUrl,
+	chUrl,
 	devUrl,
 	aboutUrl,
 	statsUrl,
diff --git a/src/web/app/desktop/router.js b/src/web/app/desktop/router.js
index df67bb7b7c..977e3fa9a6 100644
--- a/src/web/app/desktop/router.js
+++ b/src/web/app/desktop/router.js
@@ -10,8 +10,6 @@ export default me => {
 	route('/',                 index);
 	route('/selectdrive',      selectDrive);
 	route('/i>mentions',       mentions);
-	route('/channel',          channels);
-	route('/channel/:channel', channel);
 	route('/post::post',       post);
 	route('/search::query',    search);
 	route('/:user',            user.bind(null, 'home'));
@@ -57,16 +55,6 @@ export default me => {
 		mount(el);
 	}
 
-	function channel(ctx) {
-		const el = document.createElement('mk-channel-page');
-		el.setAttribute('id', ctx.params.channel);
-		mount(el);
-	}
-
-	function channels() {
-		mount(document.createElement('mk-channels-page'));
-	}
-
 	function selectDrive() {
 		mount(document.createElement('mk-selectdrive-page'));
 	}
diff --git a/src/web/app/desktop/tags/index.js b/src/web/app/desktop/tags/index.js
index 0b92d8c236..37fdfe37e4 100644
--- a/src/web/app/desktop/tags/index.js
+++ b/src/web/app/desktop/tags/index.js
@@ -61,8 +61,6 @@ require('./pages/user.tag');
 require('./pages/post.tag');
 require('./pages/search.tag');
 require('./pages/not-found.tag');
-require('./pages/channel.tag');
-require('./pages/channels.tag');
 require('./pages/selectdrive.tag');
 require('./autocomplete-suggestion.tag');
 require('./progress-dialog.tag');
diff --git a/src/web/app/desktop/tags/pages/channels.tag b/src/web/app/desktop/tags/pages/channels.tag
deleted file mode 100644
index 220f1ca50e..0000000000
--- a/src/web/app/desktop/tags/pages/channels.tag
+++ /dev/null
@@ -1,28 +0,0 @@
-<mk-channels-page>
-	<mk-ui ref="ui">
-		<main>
-			<button onclick={ parent.new }>%i18n:desktop.tags.mk-channels-page.new%</button>
-		</main>
-	</mk-ui>
-	<style>
-		:scope
-			display block
-
-	</style>
-	<script>
-		this.mixin('api');
-
-		this.on('mount', () => {
-		});
-
-		this.new = () => {
-			const title = window.prompt('%i18n:desktop.tags.mk-channels-page.channel-title%');
-
-			this.api('channels/create', {
-				title: title
-			}).then(channel => {
-				location.href = '/channel/' + channel.id;
-			});
-		};
-	</script>
-</mk-channels-page>
diff --git a/src/web/app/desktop/tags/timeline.tag b/src/web/app/desktop/tags/timeline.tag
index 17b2c66dc8..64b64f902f 100644
--- a/src/web/app/desktop/tags/timeline.tag
+++ b/src/web/app/desktop/tags/timeline.tag
@@ -112,7 +112,7 @@
 			</header>
 			<div class="body">
 				<div class="text" ref="text">
-					<p class="channel" if={ p.channel != null }><a href={ '/channel/' + p.channel.id }>{ p.channel.title }</a>:</p>
+					<p class="channel" if={ p.channel != null }><a href={ CONFIG.chUrl + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p>
 					<a class="reply" if={ p.reply_to }>
 						<i class="fa fa-reply"></i>
 					</a>
diff --git a/src/web/app/desktop/tags/ui.tag b/src/web/app/desktop/tags/ui.tag
index 7527358dce..3123c34f4f 100644
--- a/src/web/app/desktop/tags/ui.tag
+++ b/src/web/app/desktop/tags/ui.tag
@@ -335,10 +335,10 @@
 				</a>
 			</li>
 		</virtual>
-		<li class="channels">
-			<a href={ CONFIG.url + '/channel' }>
+		<li class="ch">
+			<a href={ CONFIG.chUrl } target="_blank">
 				<i class="fa fa-television"></i>
-				<p>%i18n:desktop.tags.mk-ui-header-nav.channels%</p>
+				<p>%i18n:desktop.tags.mk-ui-header-nav.ch%</p>
 			</a>
 		</li>
 		<li class="info">
diff --git a/src/web/app/mobile/tags/timeline.tag b/src/web/app/mobile/tags/timeline.tag
index b26a5cb108..ad18521df6 100644
--- a/src/web/app/mobile/tags/timeline.tag
+++ b/src/web/app/mobile/tags/timeline.tag
@@ -164,7 +164,7 @@
 			</header>
 			<div class="body">
 				<div class="text" ref="text">
-					<p class="channel" if={ p.channel != null }><a href={ '/channel/' + p.channel.id }>{ p.channel.title }</a>:</p>
+					<p class="channel" if={ p.channel != null }><a href={ CONFIG.chUrl + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p>
 					<a class="reply" if={ p.reply_to }>
 						<i class="fa fa-reply"></i>
 					</a>
diff --git a/src/web/app/mobile/tags/ui.tag b/src/web/app/mobile/tags/ui.tag
index fb8cbcdbd2..b2d96f6b8b 100644
--- a/src/web/app/mobile/tags/ui.tag
+++ b/src/web/app/mobile/tags/ui.tag
@@ -231,10 +231,11 @@
 				<li><a href="/i/messaging"><i class="fa fa-comments-o"></i>%i18n:mobile.tags.mk-ui-nav.messaging%<i class="i fa fa-circle" if={ hasUnreadMessagingMessages }></i><i class="fa fa-angle-right"></i></a></li>
 			</ul>
 			<ul>
-				<li><a onclick={ search }><i class="fa fa-search"></i>%i18n:mobile.tags.mk-ui-nav.search%<i class="fa fa-angle-right"></i></a></li>
+				<li><a href={ CONFIG.chUrl } target="_blank"><i class="fa fa-television"></i>%i18n:mobile.tags.mk-ui-nav.ch%<i class="fa fa-angle-right"></i></a></li>
+				<li><a href="/i/drive"><i class="fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="fa fa-angle-right"></i></a></li>
 			</ul>
 			<ul>
-				<li><a href="/i/drive"><i class="fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="fa fa-angle-right"></i></a></li>
+				<li><a onclick={ search }><i class="fa fa-search"></i>%i18n:mobile.tags.mk-ui-nav.search%<i class="fa fa-angle-right"></i></a></li>
 			</ul>
 			<ul>
 				<li><a href="/i/settings"><i class="fa fa-cog"></i>%i18n:mobile.tags.mk-ui-nav.settings%<i class="fa fa-angle-right"></i></a></li>
diff --git a/webpack/webpack.config.ts b/webpack/webpack.config.ts
index 5199285d55..066df18157 100644
--- a/webpack/webpack.config.ts
+++ b/webpack/webpack.config.ts
@@ -16,6 +16,7 @@ module.exports = langs.map(([lang, locale]) => {
 	const entry = {
 		desktop: './src/web/app/desktop/script.js',
 		mobile: './src/web/app/mobile/script.js',
+		ch: './src/web/app/ch/script.js',
 		stats: './src/web/app/stats/script.js',
 		status: './src/web/app/status/script.js',
 		dev: './src/web/app/dev/script.js',