diff --git a/examples/custom-parsers/README.md b/examples/custom-parsers/README.md
new file mode 100644
index 0000000000..be00e9f3b8
--- /dev/null
+++ b/examples/custom-parsers/README.md
@@ -0,0 +1,50 @@
+
+# Socket.IO custom parsers
+
+Since Socket.IO version 2.0.0, you can provide your custom parser, according to the needs of your application.
+
+Several parsers are showcased here:
+
+- the default one: [socket.io-parser](https://github.com/socketio/socket.io-parser)
+- one based on msgpack: [socket.io-msgpack-parser](https://github.com/darrachequesne/socket.io-msgpack-parser)
+- one based on native JSON: [socket.io-json-parser](https://github.com/darrachequesne/socket.io-json-parser)
+- a custom one based on [schemapack](https://github.com/phretaddin/schemapack)
+
+They are tested with various payloads:
+
+- string: `['1', '2', ... '1000']`
+- numeric: `[1, 2, ... 1000]`
+- binary: `new Buffer(1000), where buf[i] = i`
+
+## How to use
+
+```
+$ npm i && npm start
+```
+
+## Results
+
+| bytes / packet | CONNECT packet | string | numeric | binary |
+|----------------|----------------|--------|---------|-----------|
+| default | 1 | 5903 | 3904 | 43 + 1000 |
+| msgpack | 20 | 3919 | 2646 | 1029 |
+| JSON | 20 | 5930 | 3931 | 3625 |
+| schemapack | 20 | 3895 | 2005 | 1005 |
+
+## Comparison
+
+`default parser`
+- supports any serializable datastructure, including Blob and File
+- **but** binary payload is encoded as 2 packets
+
+`msgpack`
+- the size of payloads containing mostly numeric values will be greatly reduced
+- **but** rely on [ArrayBuffer](https://caniuse.com/#feat=typedarrays) in the browser (IE > 9)
+
+`JSON`
+- optimized
+- **but** does not support binary payloads
+
+`schemapack`
+- the most efficient in both speed and size
+- **but** you have to provide a schema for each packet
diff --git a/examples/custom-parsers/package.json b/examples/custom-parsers/package.json
new file mode 100644
index 0000000000..be724174f3
--- /dev/null
+++ b/examples/custom-parsers/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "parsers",
+ "version": "1.0.0",
+ "description": "Various socket.io parsers",
+ "scripts": {
+ "build": "webpack --config ./support/webpack.config.js",
+ "start": "npm run build && node ./src/server.js"
+ },
+ "author": "Damien Arrachequesne",
+ "license": "MIT",
+ "dependencies": {
+ "component-emitter": "^1.2.1",
+ "express": "^4.15.2",
+ "schemapack": "^1.4.2",
+ "socket.io": "socketio/socket.io",
+ "socket.io-client": "socketio/socket.io-client",
+ "socket.io-json-parser": "^1.0.0",
+ "socket.io-msgpack-parser": "^1.0.0",
+ "webpack": "^2.4.1"
+ }
+}
diff --git a/examples/custom-parsers/public/index.html b/examples/custom-parsers/public/index.html
new file mode 100644
index 0000000000..a0cb336744
--- /dev/null
+++ b/examples/custom-parsers/public/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ Socket.IO custom parsers
+
+
+
+
+
+
+
+
diff --git a/examples/custom-parsers/src/client1.js b/examples/custom-parsers/src/client1.js
new file mode 100644
index 0000000000..f73f775c64
--- /dev/null
+++ b/examples/custom-parsers/src/client1.js
@@ -0,0 +1,8 @@
+
+const socket = require('socket.io-client')('localhost:3001', {});
+
+socket.io.engine.on('data', (data) => console.log('[default]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));
+
+socket.on('string', (data) => console.log('[default] [string]', data));
+socket.on('numeric', (data) => console.log('[default] [numeric]', data));
+socket.on('binary', (data) => console.log('[default] [binary]', data));
diff --git a/examples/custom-parsers/src/client2.js b/examples/custom-parsers/src/client2.js
new file mode 100644
index 0000000000..a98af8b1d2
--- /dev/null
+++ b/examples/custom-parsers/src/client2.js
@@ -0,0 +1,11 @@
+
+const customParser = require('socket.io-msgpack-parser');
+const socket = require('socket.io-client')('http://localhost:3002', {
+ parser: customParser
+});
+
+socket.io.engine.on('data', (data) => console.log('[msgpack]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));
+
+socket.on('string', (data) => console.log('[msgpack] [string]', data));
+socket.on('numeric', (data) => console.log('[msgpack] [numeric]', data));
+socket.on('binary', (data) => console.log('[msgpack] [binary]', data));
diff --git a/examples/custom-parsers/src/client3.js b/examples/custom-parsers/src/client3.js
new file mode 100644
index 0000000000..9bfb6c68dd
--- /dev/null
+++ b/examples/custom-parsers/src/client3.js
@@ -0,0 +1,11 @@
+
+const customParser = require('socket.io-json-parser');
+const socket = require('socket.io-client')('localhost:3003', {
+ parser: customParser
+});
+
+socket.io.engine.on('data', (data) => console.log('[json]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));
+
+socket.on('string', (data) => console.log('[json] [string]', data));
+socket.on('numeric', (data) => console.log('[json] [numeric]', data));
+socket.on('binary', (data) => console.log('[json] [binary]', data));
diff --git a/examples/custom-parsers/src/client4.js b/examples/custom-parsers/src/client4.js
new file mode 100644
index 0000000000..b44168ac17
--- /dev/null
+++ b/examples/custom-parsers/src/client4.js
@@ -0,0 +1,11 @@
+
+const customParser = require('./custom-parser');
+const socket = require('socket.io-client')('localhost:3004', {
+ parser: customParser
+});
+
+socket.io.engine.on('data', (data) => console.log('[custom]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));
+
+socket.on('string', (data) => console.log('[custom] [string]', data));
+socket.on('numeric', (data) => console.log('[custom] [numeric]', data));
+socket.on('binary', (data) => console.log('[custom] [binary]', data));
diff --git a/examples/custom-parsers/src/custom-parser.js b/examples/custom-parsers/src/custom-parser.js
new file mode 100644
index 0000000000..74c6b42ce6
--- /dev/null
+++ b/examples/custom-parsers/src/custom-parser.js
@@ -0,0 +1,125 @@
+
+const Emitter = require('component-emitter');
+const schemapack = require('schemapack');
+
+/**
+ * Packet types (see https://github.com/socketio/socket.io-protocol)
+ */
+
+const TYPES = {
+ CONNECT: 0,
+ DISCONNECT: 1,
+ EVENT: 2,
+ ACK: 3,
+ ERROR: 4,
+ BINARY_EVENT: 5,
+ BINARY_ACK: 6
+};
+
+const stringSchema = schemapack.build({
+ _id: 'uint8',
+ data: [ 'string' ],
+ nsp: 'string'
+});
+
+const numericSchema = schemapack.build({
+ _id: 'uint8',
+ data: [ 'uint16' ],
+ nsp: 'string'
+});
+
+const binarySchema = schemapack.build({
+ _id: 'uint8',
+ data: 'buffer',
+ nsp: 'string'
+});
+
+const errorPacket = {
+ type: TYPES.ERROR,
+ data: 'parser error'
+};
+
+class Encoder {
+ encode (packet, callback) {
+ switch (packet.type) {
+ case TYPES.EVENT:
+ return callback([ this.pack(packet) ]);
+ default:
+ return callback([ JSON.stringify(packet) ]);
+ }
+ }
+ pack (packet) {
+ let eventName = packet.data[0];
+ let flatPacket = {
+ data: packet.data[1],
+ nsp: packet.nsp
+ };
+ switch (eventName) {
+ case 'string':
+ flatPacket._id = 1;
+ return stringSchema.encode(flatPacket);
+ case 'numeric':
+ flatPacket._id = 2;
+ return numericSchema.encode(flatPacket);
+ case 'binary':
+ flatPacket._id = 3;
+ return binarySchema.encode(flatPacket);
+ default:
+ throw new Error('unknown event name: ' + eventName);
+ }
+ }
+}
+
+class Decoder extends Emitter {
+ add (obj) {
+ if (typeof obj === 'string') {
+ this.parseJSON(obj);
+ } else {
+ this.parseBinary(obj);
+ }
+ }
+ parseJSON (obj) {
+ try {
+ let decoded = JSON.parse(obj);
+ this.emit('decoded', decoded);
+ } catch (e) {
+ this.emit('decoded', errorPacket);
+ }
+ }
+ parseBinary (obj) {
+ let view = new Uint8Array(obj);
+ let packetId = view[0];
+ try {
+ let packet = {
+ type: TYPES.EVENT
+ };
+ let decoded;
+ switch (packetId) {
+ case 1:
+ decoded = stringSchema.decode(obj);
+ packet.data = [ 'string', decoded.data ];
+ packet.nsp = decoded.nsp;
+ break;
+ case 2:
+ decoded = numericSchema.decode(obj);
+ packet.data = [ 'numeric', decoded.data ];
+ packet.nsp = decoded.nsp;
+ break;
+ case 3:
+ decoded = binarySchema.decode(obj);
+ packet.data = [ 'binary', decoded.data.buffer ];
+ packet.nsp = decoded.nsp;
+ break;
+ default:
+ throw new Error('unknown type');
+ }
+ this.emit('decoded', packet);
+ } catch (e) {
+ this.emit('decoded', errorPacket);
+ }
+ }
+ destroy () {}
+}
+
+exports.Encoder = Encoder;
+exports.Decoder = Decoder;
diff --git a/examples/custom-parsers/src/server.js b/examples/custom-parsers/src/server.js
new file mode 100644
index 0000000000..3b20d1703d
--- /dev/null
+++ b/examples/custom-parsers/src/server.js
@@ -0,0 +1,55 @@
+
+const express = require('express');
+const app = express();
+const server = require('http').createServer(app);
+const path = require('path');
+const port = process.env.PORT || 3000;
+
+app.use(express.static(path.join(__dirname, '../public')));
+
+server.listen(port, () => console.log('>>> http://localhost:' + port));
+
+const io = require('socket.io');
+const msgpackParser = require('socket.io-msgpack-parser');
+const jsonParser = require('socket.io-json-parser');
+const customParser = require('./custom-parser');
+
+let server1 = io(3001, {});
+let server2 = io(3002, {
+ parser: msgpackParser
+});
+let server3 = io(3003, {
+ parser: jsonParser
+});
+let server4 = io(3004, {
+ parser: customParser
+});
+
+let string = [];
+let numeric = [];
+let binary = new Buffer(1e3);
+for (var i = 0; i < 1e3; i++) {
+ string.push('' + i);
+ numeric.push(i);
+ binary[i] = i;
+}
+
+server1.on('connect', onConnect(1000));
+server2.on('connect', onConnect(2000));
+server3.on('connect', onConnect(3000));
+server4.on('connect', onConnect(4000));
+
+function onConnect (delay) {
+ return function (socket) {
+ console.log('connect ' + socket.id);
+
+ setTimeout(() => {
+ socket.emit('string', string);
+ socket.emit('numeric', numeric);
+ socket.emit('binary', binary);
+ }, delay);
+
+ socket.on('disconnect', () => console.log('disconnect ' + socket.id));
+ };
+}
+
diff --git a/examples/custom-parsers/support/webpack.config.js b/examples/custom-parsers/support/webpack.config.js
new file mode 100644
index 0000000000..34dcc13308
--- /dev/null
+++ b/examples/custom-parsers/support/webpack.config.js
@@ -0,0 +1,15 @@
+
+const path = require('path');
+
+module.exports = {
+ entry: {
+ client1: './src/client1.js',
+ client2: './src/client2.js',
+ client3: './src/client3.js',
+ client4: './src/client4.js'
+ },
+ output: {
+ path: path.resolve(__dirname, '../public'),
+ filename: '[name].bundle.js'
+ }
+};