Index: wp-includes/css/media-views.css
===================================================================
--- wp-includes/css/media-views.css	(revision 22987)
+++ wp-includes/css/media-views.css	(working copy)
@@ -693,7 +693,7 @@
 	background-position: -60px 0;
 }
 
-.media-frame .describe {
+.media-frame .attachment .describe {
 	position: relative;
 	display: block;
 	width: 100%;
@@ -1161,9 +1161,42 @@
 }
 
 /**
+ * Spinner
+ */
+.media-sidebar .settings-save-status {
+	background: #f5f5f5;
+	float: right;
+	text-transform: none;
+	z-index: 10;
+}
+
+.media-sidebar .settings-save-status .spinner {
+	background: url(../../wp-admin/images/wpspin_light.gif) no-repeat;
+	background-size: 16px 16px;
+	display: none;
+	float: right;
+	opacity: 0.7;
+	filter: alpha(opacity=70);
+	width: 16px;
+	height: 16px;
+	margin: 0 5px 0;
+}
+
+.media-sidebar .settings-save-status .saved {
+	float: right;
+	display: none;
+}
+
+.media-sidebar .save-waiting .settings-save-status .spinner,
+.media-sidebar .save-complete .settings-save-status .saved {
+	display: block;
+}
+
+/**
  * Attachment Details
  */
 .attachment-details {
+	position: relative;
 	overflow: auto;
 }
 
Index: wp-includes/js/media-views.js
===================================================================
--- wp-includes/js/media-views.js	(revision 22987)
+++ wp-includes/js/media-views.js	(working copy)
@@ -2584,6 +2584,9 @@
 			// Check if the model is selected.
 			this.updateSelect();
 
+			// Update the save status.
+			this.updateSave();
+
 			this.views.render();
 			return this;
 		},
@@ -2688,9 +2691,51 @@
 			value   = event.target.value;
 
 			if ( this.model.get( setting ) !== value )
-				this.model.save( setting, value );
+				this.save( setting, value );
 		},
 
+		// Pass all the arguments to the model's save method.
+		//
+		// Records the aggregate status of all save requests and updates the
+		// view's classes accordingly.
+		save: function() {
+			var view = this,
+				save = this._save = this._save || { status: 'ready' },
+				request = this.model.save.apply( this.model, arguments ),
+				requests = save.requests ? $.when( request, save.requests ) : request;
+
+			// If we're waiting to remove 'Saved.', stop.
+			if ( save.savedTimer )
+				clearTimeout( save.savedTimer );
+
+			this.updateSave('waiting');
+			save.requests = requests;
+			requests.done( function() {
+				// If we've performed another request since this one, bail.
+				if ( save.requests !== requests )
+					return;
+
+				view.updateSave('complete');
+				save.savedTimer = setTimeout( function() {
+					view.updateSave('ready');
+					delete save.savedTimer;
+				}, 2000 );
+			});
+
+		},
+
+		updateSave: function( status ) {
+			var save = this._save = this._save || { status: 'ready' };
+
+			if ( status && status !== save.status ) {
+				this.$el.removeClass( 'save-' + save.status );
+				save.status = status;
+			}
+
+			this.$el.addClass( 'save-' + save.status );
+			return this;
+		},
+
 		updateAll: function() {
 			var $settings = this.$('[data-setting]'),
 				model = this.model,
@@ -3835,4 +3880,4 @@
 			this.$('img').attr( 'src', this.model.get('url') );
 		}
 	});
-}(jQuery));
\ No newline at end of file
+}(jQuery));
Index: wp-includes/media.php
===================================================================
--- wp-includes/media.php	(revision 22987)
+++ wp-includes/media.php	(working copy)
@@ -1665,7 +1665,14 @@
 	</script>
 
 	<script type="text/html" id="tmpl-attachment-details">
-		<h3><?php _e('Attachment Details'); ?></h3>
+		<h3>
+			<?php _e('Attachment Details'); ?>
+
+			<span class="settings-save-status">
+				<span class="spinner"></span>
+				<span class="saved"><?php esc_html_e('Saved.'); ?></span>
+			</span>
+		</h3>
 		<div class="attachment-info">
 			<div class="thumbnail">
 				<# if ( data.uploading ) { #>
