Index: src/wp-admin/admin-header.php
===================================================================
--- src/wp-admin/admin-header.php	(revision 31467)
+++ src/wp-admin/admin-header.php	(working copy)
@@ -189,6 +189,8 @@
 }
 ?>
 
+<div id="wp-speak" aria-live="polite" aria-relevant="all" aria-role="status" aria-atomic="true" class="screen-reader-text"></div>
+
 <div id="wpwrap">
 <a tabindex="1" href="#wpbody-content" class="screen-reader-shortcut"><?php _e('Skip to main content'); ?></a>
 <?php require(ABSPATH . 'wp-admin/menu-header.php'); ?>
Index: src/wp-admin/admin.php
===================================================================
--- src/wp-admin/admin.php	(revision 31467)
+++ src/wp-admin/admin.php	(working copy)
@@ -93,6 +93,7 @@
 $time_format = get_option('time_format');
 
 wp_enqueue_script( 'common' );
+wp_enqueue_script( 'a11y' );
 
 // $pagenow is set in vars.php
 // $wp_importers is sometimes set in wp-admin/includes/import.php
Index: src/wp-admin/customize.php
===================================================================
--- src/wp-admin/customize.php	(revision 31467)
+++ src/wp-admin/customize.php	(working copy)
@@ -118,7 +118,7 @@
 <body class="<?php echo esc_attr( $body_class ); ?>">
 <div class="wp-full-overlay expanded">
 	<form id="customize-controls" class="wrap wp-full-overlay-sidebar">
-		<div id="screen-reader-messages" aria-live="polite" aria-relevant="all" aria-role="status" aria-atomic="true" class="screen-reader-text"></div>
+		<div id="wp-speak" aria-live="polite" aria-relevant="all" aria-role="status" aria-atomic="true" class="screen-reader-text"></div>
 
 		<div id="customize-header-actions" class="wp-full-overlay-header">
 			<?php
Index: src/wp-admin/js/customize-widgets.js
===================================================================
--- src/wp-admin/js/customize-widgets.js	(revision 31467)
+++ src/wp-admin/js/customize-widgets.js	(working copy)
@@ -685,10 +685,12 @@
 
 					if ( isMoveUp ) {
 						self.moveUp();
-						$( '#screen-reader-messages' ).text( l10n.widgetMovedUp );
+						wp.speak( l10n.widgetMovedUp );
+						wp.webSpeech( l10n.widgetMovedUp, 0 );
 					} else {
 						self.moveDown();
-						$( '#screen-reader-messages' ).text( l10n.widgetMovedDown );
+						wp.speak( l10n.widgetMovedDown );
+						wp.webSpeech( l10n.widgetMovedDown, 1 );
 					}
 
 					$( this ).focus(); // re-focus after the container was moved
@@ -1987,4 +1989,7 @@
 		return settingId;
 	}
 
+	wp.a11y.debug = true;
+	wp.a11y.enableWebSpeech = true;
+
 })( window.wp, jQuery );
Index: src/wp-admin/js/updates.js
===================================================================
--- src/wp-admin/js/updates.js	(revision 31467)
+++ src/wp-admin/js/updates.js	(working copy)
@@ -107,6 +107,8 @@
 
 		$message.addClass( 'updating-message' );
 		$message.text( wp.updates.l10n.updating );
+		wp.speak( wp.updates.l10n.updatingMsg );
+		wp.webSpeech( wp.updates.l10n.updatingMsg );
 
 		if ( wp.updates.updateLock ) {
 			wp.updates.updateQueue.push( {
@@ -153,6 +155,8 @@
 
 		$message.removeClass( 'updating-message' ).addClass( 'updated-message' );
 		$message.text( wp.updates.l10n.updated );
+		wp.speak( wp.updates.l10n.updatedMsg );
+		wp.webSpeech( wp.updates.l10n.updatedMsg, 'Google UK English Male' );
 
 		wp.updates.decrementCount( 'plugin' );
 	};
@@ -173,6 +177,8 @@
 		}
 		$message.removeClass( 'updating-message' );
 		$message.text( wp.updates.l10n.updateFailed );
+		wp.speak( wp.updates.l10n.updateFailed );
+		wp.webSpeech( wp.updates.l10n.updateFailed, 4 );
 	};
 
 	/**
@@ -198,6 +204,8 @@
 
 		$message.addClass( 'updating-message' );
 		$message.text( wp.updates.l10n.installing );
+		wp.speak( wp.updates.l10n.installingMsg );
+		wp.webSpeech( wp.updates.l10n.installingMsg, 2 );
 
 		if ( wp.updates.updateLock ) {
 			wp.updates.updateQueue.push( {
@@ -234,6 +242,8 @@
 
 		$message.removeClass( 'updating-message' ).addClass( 'updated-message button-disabled' );
 		$message.text( wp.updates.l10n.installed );
+		wp.speak( wp.updates.l10n.installedMsg );
+		wp.webSpeech( wp.updates.l10n.installedMsg, 3 );
 	};
 
 	/**
@@ -316,6 +326,9 @@
 			}
 			wp.updates.installPlugin( $button.data( 'slug' ) );
 		} );
+
+		wp.a11y.debug = true;
+		wp.a11y.enableWebSpeech = true;
 	} );
 
 	$( window ).on( 'message', function( e ) {
Index: src/wp-includes/js/wp-a11y.js
===================================================================
--- src/wp-includes/js/wp-a11y.js	(revision 0)
+++ src/wp-includes/js/wp-a11y.js	(working copy)
@@ -0,0 +1,157 @@
+window.wp = window.wp || {};
+
+( function ( wp, $ ) {
+	'use strict';
+
+	var a11y;
+
+	a11y = wp.a11y = wp.a11y || {};
+
+	/**
+	 * Flag to enable debug mode.
+	 * Set this to true in your script on document.ready to have messages
+	 * printed out in your browser's console.
+	 *
+	 * @since 4.2.0
+	 *
+	 * @var bool
+	 */
+	a11y.debug = false;
+
+	/**
+	 * Flag to enable Web Speech API experimental support.
+	 *
+	 * @since 4.2.0
+	 *
+	 * @var bool
+	 */
+	a11y.enableWebSpeech = false;
+
+	/**
+	 * wp.a11y.log
+	 *
+	 * A debugging utility. Works only when the debug flag is on and the browser
+	 * supports it.
+	 *
+	 * @since 4.2.0
+	 */
+	a11y.log = function() {
+		if ( window.console && a11y.debug ) {
+			window.console.log.apply( window.console, arguments );
+		}
+	};
+
+	/**
+	 * wp.a11y.speak
+	 *
+	 * Update the ARIA live notification area text node.
+	 *
+	 * @since 4.2.0
+	 *
+	 * @param {string} message
+	 */
+	a11y.speak = function( message ) {
+
+		if ( 0 === a11y.liveContainer.length ) {
+			// No need to make this translatable, it's just for debugging.
+			a11y.log( 'WP speak can\'t speak: no live notification area present' );
+			return;
+		}
+
+		// Log messages to the console when debugging.
+		a11y.log( 'WP speak: ' + message );
+
+		// Make messages available to screen readers.
+		a11y.liveContainer.text( message );
+
+	};
+
+	/**
+	 * wp.a11y.setWebSpeechVoices
+	 *
+	 * Set up voices for the Web Speech API if enabled and the browser supports it.
+	 *
+	 * @since 4.2.0
+	 */
+	a11y.setWebSpeechVoices = function() {
+
+		var logged = false;
+
+		if ( 'speechSynthesis' in window && a11y.enableWebSpeech ) {
+			// Voices are loaded on demand, asynchronously, the first time you call getVoices.
+			// When they load, an onvoiceschanged event is fired.
+			window.speechSynthesis.onvoiceschanged = function() {
+				a11y.voices = window.speechSynthesis.getVoices();
+				// Log in the console the available Web Speech API voices when debugging.
+				if ( a11y.debug && ! logged ) {
+					a11y.voices.forEach( function( voice ) {
+						window.console.log( voice.name, voice['default'] ? '(browser default)' : '' );
+					});
+					// Log them just once.
+					logged = true;
+				}
+			};
+		}
+	};
+
+	/**
+	 * wp.a11y.webSpeech
+	 *
+	 * Deliver messages to the Web Speech API if enabled and the browser supports it.
+	 *
+	 * @since 4.2.0
+	 *
+	 * @param {string} message
+	 * @param {number|string} setVoice voice array index or voice name
+	 */
+	a11y.webSpeech = function( message, setVoice ) {
+
+		var utterance;
+
+		if ( 'speechSynthesis' in window && a11y.enableWebSpeech ) {
+			utterance = new window.SpeechSynthesisUtterance( message );
+			// Switch voice.
+			if ( setVoice && 'number' === typeof( setVoice ) ) {
+				utterance.voice = a11y.voices[ setVoice ];
+			} else if ( setVoice && 'string' === typeof( setVoice ) ) {
+				utterance.voice = a11y.voices.filter( function( voice ) {
+					return voice.name == setVoice;
+				})[0];
+			} else {
+				utterance.voice = a11y.voices[0];
+			}
+			window.speechSynthesis.speak( utterance );
+		}
+	};
+
+	/**
+	 * Give developers an easy, handy, shortcut to send messages to screen readers.
+	 *
+	 * Usage: wp.speak( 'your text string here' );
+	 *
+	 * @since 4.2.0
+	 */
+	wp.speak = a11y.speak;
+
+	/**
+	 * Web Speech API shortcut
+	 *
+	 * Usage: wp.webSpeech( 'your text string here' );
+	 *
+	 * @since 4.2.0
+	 */
+	wp.webSpeech = a11y.webSpeech;
+
+	/**
+	 * Initialize wp.a11y and define ARIA live notification area.
+	 *
+	 * @since 4.2.0
+	 */
+	a11y.init = function() {
+		a11y.liveContainer = $( '#wp-speak' );
+		a11y.setWebSpeechVoices();
+	};
+
+	$( document ).ready( a11y.init );
+
+} ( window.wp, jQuery ) );
Index: src/wp-includes/script-loader.php
===================================================================
--- src/wp-includes/script-loader.php	(revision 31467)
+++ src/wp-includes/script-loader.php	(working copy)
@@ -82,6 +82,8 @@
 		'warnDelete' => __("You are about to permanently delete the selected items.\n  'Cancel' to stop, 'OK' to delete.")
 	) );
 
+	$scripts->add( 'a11y', "/wp-includes/js/wp-a11y$suffix.js", array( 'jquery' ), false, 1 );
+
 	$scripts->add( 'sack', "/wp-includes/js/tw-sack$suffix.js", array(), '1.6.1', 1 );
 
 	$scripts->add( 'quicktags', "/wp-includes/js/quicktags$suffix.js", array(), false, 1 );
@@ -379,7 +381,7 @@
 
 	$scripts->add( 'hoverIntent', "/wp-includes/js/hoverIntent$suffix.js", array('jquery'), 'r7', 1 );
 
-	$scripts->add( 'customize-base',     "/wp-includes/js/customize-base$suffix.js",     array( 'jquery', 'json2', 'underscore' ), false, 1 );
+	$scripts->add( 'customize-base',     "/wp-includes/js/customize-base$suffix.js",     array( 'jquery', 'json2', 'underscore', 'a11y' ), false, 1 );
 	$scripts->add( 'customize-loader',   "/wp-includes/js/customize-loader$suffix.js",   array( 'customize-base' ), false, 1 );
 	$scripts->add( 'customize-preview',  "/wp-includes/js/customize-preview$suffix.js",  array( 'customize-base' ), false, 1 );
 	$scripts->add( 'customize-models',   "/wp-includes/js/customize-models.js", array( 'underscore', 'backbone' ), false, 1 );
@@ -511,6 +513,10 @@
 				'installing'    => __( 'Installing...' ),
 				'installed'     => __( 'Installed!' ),
 				'installFailed' => __( 'Installation failed' ),
+				'updatingMsg'   => __( 'Updating... please wait.' ),
+				'installingMsg' => __( 'Installing... please wait.' ),
+				'updatedMsg'    => __( 'Update completed successfully.' ),
+				'installedMsg'  => __( 'Installation completed successfully.' ),
 			)
 		) );
 
