diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000000000000000000000000000000000000..fe8094878e37b30ef304d99cb9c80a7998764625
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,22 @@
+{
+    "curly": false,
+    "eqeqeq": true,
+    "immed": true,
+    "esnext": true,
+    "latedef": "nofunc",
+    "newcap": true,
+    "noarg": true,
+    "sub": true,
+    "undef": true,
+    "eqnull": true,
+    "browser": true,
+    "expr": true,
+    "globals": {
+        "head": false,
+        "module": false,
+        "console": false,
+        "unescape": false,
+        "define": false,
+        "exports": false
+    }
+}
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000000000000000000000000000000000000..a44ae9b29b5bdaad30119110a5310f7c00f6d68b
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,58 @@
+const gulp = require('gulp')
+const jshint = require('gulp-jshint')
+const uglify = require('gulp-uglify')
+const rename = require('gulp-rename')
+const sass = require('gulp-sass')
+const autoprefixer = require('gulp-autoprefixer')
+const minify = require('gulp-clean-css')
+const qunit = require('gulp-qunit')
+const zip = require('gulp-zip')
+const connect = require('gulp-connect')
+
+gulp.task('js', function () {
+    return gulp.src(['./js/reveal.js']).pipe(uglify()).pipe(rename('reveal.min.js')).pipe(gulp.dest('./js'))
+})
+
+gulp.task('css-themes', function () {
+    return gulp.src(['./css/theme/source/*.{sass,scss}']).pipe(sass()).pipe(gulp.dest('./css/theme'))
+})
+
+gulp.task('css-core', gulp.series(function () {
+    return gulp.src(['css/reveal.scss']).pipe(sass()).pipe(autoprefixer()).pipe(gulp.dest('./css'))
+}, function () {
+    return gulp.src(['css/reveal.css']).pipe(minify({
+        compatibility: 'ie9'
+    })).pipe(rename('reveal.min.css')).pipe(gulp.dest('./css'))
+}))
+
+gulp.task('css', gulp.parallel('css-themes', 'css-core'))
+
+gulp.task('test', gulp.parallel(function () {
+    return gulp.src(['./js/reveal.js']).pipe(jshint()).pipe(jshint.reporter('default')).pipe(jshint.reporter('fail'));
+}, function () {
+    return gulp.src(['./test/*.html']).pipe(qunit())
+}))
+
+gulp.task('default', gulp.series(gulp.parallel('js', 'css'), 'test'))
+
+gulp.task('package', gulp.series('default', function () {
+    return gulp.src([
+        './index.html',
+        './css/**',
+        './js/**',
+        './lib/**',
+        './images/**',
+        './plugin/**',
+        './**.md'
+    ]).pipe(zip('reveal-js-presentation.zip')).pipe(gulp.dest('./'))
+}))
+
+gulp.task('serve', function () {
+    connect.server({
+        root: '.',
+        livereload: true,
+        open: true,
+        useAvailablePort: true
+    })
+    gulp.watch(['js/reveal.js'], gulp.series('js'))
+})
\ No newline at end of file
diff --git a/package.json b/package.json
index 9bb5d29196eee92ceb70a61bbd6f7eed32ef1348..c4efa30e6c3f4dce5a6cc3409d7e94d83c8c398f 100644
--- a/package.json
+++ b/package.json
@@ -6,9 +6,9 @@
   "subdomain": "revealjs",
   "main": "js/reveal.js",
   "scripts": {
-    "test": "grunt test",
-    "start": "grunt serve",
-    "build": "grunt"
+    "test": "gulp test",
+    "start": "gulp serve",
+    "build": "gulp"
   },
   "author": {
     "name": "Hakim El Hattab",
@@ -33,11 +33,25 @@
     "grunt-contrib-qunit": "^2.0.0",
     "grunt-contrib-uglify": "^3.3.0",
     "grunt-contrib-watch": "^1.0.0",
-    "grunt-sass": "^2.0.0",
     "grunt-retire": "^1.0.7",
+    "grunt-sass": "^2.0.0",
     "grunt-zip": "~0.17.1",
+    "gulp": "^4.0.0",
+    "gulp-autoprefixer": "^5.0.0",
+    "gulp-clean-css": "^3.9.3",
+    "gulp-connect": "^5.5.0",
+    "gulp-jshint": "^2.1.0",
+    "gulp-qunit": "^2.0.1",
+    "gulp-rename": "^1.2.2",
+    "gulp-sass": "^3.1.0",
+    "gulp-uglify": "^3.0.0",
+    "gulp-zip": "^4.1.0",
+    "jshint": "^2.9.5",
     "mustache": "^2.3.0",
     "socket.io": "^1.7.3"
   },
-  "license": "MIT"
+  "license": "MIT",
+  "dependencies": {
+    "npm": "^5.7.1"
+  }
 }