diff --git a/README.md b/README.md
index 49a66fe6792a498e02b20af7318653000c49e605..f45e004b3bbda7f76d4cd357e213020db49f6472 100644
--- a/README.md
+++ b/README.md
@@ -522,6 +522,13 @@ You can add your own extensions using the same syntax. The following properties
 - **callback**: [optional] Function to execute when the script has loaded
 - **condition**: [optional] Function which must return true for the script to be loaded
 
+You can additionally use the following syntax, in case you are using a bundler:
+- **id**: the id of the plugin to load
+- **plugin**: the plugin object to load. It is the plugin implementation that can contain an `init` function
+- **async**: [optional] Flags if the script should load after reveal.js has started, defaults to false
+- **callback**: [optional] Function to execute when the script has loaded
+- **condition**: [optional] Function which must return true for the script to be loaded
+
 ### Ready Event
 
 A `ready` event is fired when reveal.js has loaded all non-async dependencies and is ready to start navigating. To check if reveal.js is already 'ready' you can call `Reveal.isReady()`.
diff --git a/js/controllers/plugins.js b/js/controllers/plugins.js
index 300b0ba31bf827c8a89c9b0f49d42c9ead3cd5d5..94077f7dceaece75b28236a8b4e37f7e7a5b76df 100644
--- a/js/controllers/plugins.js
+++ b/js/controllers/plugins.js
@@ -48,17 +48,22 @@ export default class Plugins {
 			if( scripts.length ) {
 				scriptsToLoad = scripts.length;
 
-				// Load synchronous scripts
-				scripts.forEach( s => {
-					loadScript( s.src, () => {
-
-						if( typeof s.callback === 'function' ) s.callback();
+				const scriptLoadedCallback = (s) => {
+					if( typeof s.callback === 'function' ) s.callback();
 
-						if( --scriptsToLoad === 0 ) {
-							this.initPlugins().then( resolve );
-						}
+					if( --scriptsToLoad === 0 ) {
+						this.initPlugins().then( resolve );
+					}
+				};
 
-					} );
+				// Load synchronous scripts
+				scripts.forEach( s => {
+					if (s.id) {
+						this.registerPlugin(s.id, s.plugin);
+						scriptLoadedCallback(s);
+					} else {
+						loadScript( s.src, () => scriptLoadedCallback(s));
+					}
 				} );
 			}
 			else {
@@ -129,7 +134,13 @@ export default class Plugins {
 
 		if( this.asyncDependencies.length ) {
 			this.asyncDependencies.forEach( s => {
-				loadScript( s.src, s.callback );
+				if (s.id) {
+					this.registerPlugin(s.id, s.plugin);
+					if (typeof s.plugin.init === 'function') { s.plugin.init(); }
+					if (typeof s.callback === 'function') { s.callback(); }
+				} else {
+					loadScript( s.src, s.callback );
+				}
 			} );
 		}
 
@@ -190,4 +201,4 @@ export default class Plugins {
 
 	}
 
-}
\ No newline at end of file
+}
diff --git a/js/reveal.js b/js/reveal.js
index 8cb521bf5ba83b61fef2a3f647e6d5e8b209f6eb..a48377b526e5bba27e85fc36f4784d6a5b7a4dd3 100644
--- a/js/reveal.js
+++ b/js/reveal.js
@@ -180,7 +180,7 @@ export default function( revealElement, options ) {
 
 		// Flags if we should use zoom instead of transform to scale
 		// up slides. Zoom produces crisper results but has a lot of
-		// xbrowser quirks so we only use it in whitelsited browsers.
+		// xbrowser quirks so we only use it in white-listed browsers.
 		features.zoom = 'zoom' in testElement.style && !isMobileDevice &&
 						( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) );
 
@@ -5675,4 +5675,4 @@ export default function( revealElement, options ) {
 
 	return Reveal;
 
-};
\ No newline at end of file
+};