diff --git src/wp-admin/css/customize-controls.css src/wp-admin/css/customize-controls.css
index 4b153a4..585878b 100644
--- src/wp-admin/css/customize-controls.css
+++ src/wp-admin/css/customize-controls.css
@@ -725,6 +725,7 @@ p.customize-section-description {
 .customize-control-background .current,
 .customize-control-cropped_image .current,
 .customize-control-site_icon .current,
+.customize-control-site_logo .current,
 .customize-control-header .current {
 	margin-bottom: 8px;
 }
@@ -766,6 +767,9 @@ p.customize-section-description {
 .customize-control-site_icon .remove-button,
 .customize-control-site_icon .default-button,
 .customize-control-site_icon .upload-button,
+.customize-control-site_logo .remove-button,
+.customize-control-site_logo .default-button,
+.customize-control-site_logo .upload-button,
 .customize-control-header button.new,
 .customize-control-header button.remove {
 	white-space: normal;
@@ -779,6 +783,7 @@ p.customize-section-description {
 .customize-control-background .current .container,
 .customize-control-cropped_image .current .container,
 .customize-control-site_icon .current .container,
+.customize-control-site_logo .current .container,
 .customize-control-header .current .container {
 	overflow: hidden;
 	-webkit-border-radius: 2px;
@@ -792,6 +797,7 @@ p.customize-section-description {
 .customize-control-background .current .container,
 .customize-control-cropped_image .current .container,
 .customize-control-site_icon .current .container,
+.customize-control-site_logo .current .container,
 .customize-control-image .current .container {
 	min-height: 40px;
 }
@@ -802,6 +808,7 @@ p.customize-section-description {
 .customize-control-background .placeholder,
 .customize-control-cropped_image .placeholder,
 .customize-control-site_icon .placeholder,
+.customize-control-site_logo .placeholder,
 .customize-control-header .placeholder {
 	width: 100%;
 	position: relative;
@@ -815,6 +822,7 @@ p.customize-section-description {
 .customize-control-background .inner,
 .customize-control-cropped_image .inner,
 .customize-control-site_icon .inner,
+.customize-control-site_logo .inner,
 .customize-control-header .inner {
 	display: none;
 	position: absolute;
@@ -830,6 +838,7 @@ p.customize-section-description {
 .customize-control-background .inner,
 .customize-control-cropped_image .inner,
 .customize-control-site_icon .inner,
+.customize-control-site_logo .inner,
 .customize-control-image .inner {
 	display: block;
 	min-height: 40px;
@@ -841,6 +850,7 @@ p.customize-section-description {
 .customize-control-background .inner,
 .customize-control-cropped_image .inner,
 .customize-control-site_icon .inner,
+.customize-control-site_logo.inner,
 .customize-control-header .inner,
 .customize-control-header .inner .dashicons {
 	line-height: 20px;
@@ -966,6 +976,7 @@ p.customize-section-description {
 .customize-control-background img,
 .customize-control-cropped_image img,
 .customize-control-site_icon img,
+.customize-control-site_logo img,
 .customize-control-header img {
 	width: 100%;
 	-webkit-border-radius: 2px;
@@ -984,6 +995,8 @@ p.customize-section-description {
 .customize-control-cropped_image .default-button,
 .customize-control-site_icon .remove-button,
 .customize-control-site_icon .default-button,
+.customize-control-site_logo .remove-button,
+.customize-control-site_logo .default-button,
 .customize-control-header .remove {
 	float: left;
 	margin-right: 3px;
@@ -995,6 +1008,7 @@ p.customize-section-description {
 .customize-control-background .upload-button,
 .customize-control-cropped_image .upload-button,
 .customize-control-site_icon .upload-button,
+.customize-control-site_logo .upload-button,
 .customize-control-header .new {
 	float: right;
 }
diff --git src/wp-admin/includes/admin.php src/wp-admin/includes/admin.php
index a44abba..5b749f9 100644
--- src/wp-admin/includes/admin.php
+++ src/wp-admin/includes/admin.php
@@ -72,6 +72,9 @@ require_once(ABSPATH . 'wp-admin/includes/user.php');
 /** WordPress Site Icon API */
 require_once(ABSPATH . 'wp-admin/includes/class-wp-site-icon.php');
 
+/** WordPress Site Logo API */
+require_once(ABSPATH . 'wp-admin/includes/class-wp-site-logo.php');
+
 /** WordPress Update Administration API */
 require_once(ABSPATH . 'wp-admin/includes/update.php');
 
diff --git src/wp-admin/includes/class-wp-site-icon.php src/wp-admin/includes/class-wp-site-icon.php
index 8c06db6..104d99c 100644
--- src/wp-admin/includes/class-wp-site-icon.php
+++ src/wp-admin/includes/class-wp-site-icon.php
@@ -64,6 +64,7 @@ class WP_Site_Icon {
 
 		// Our regular Favicon.
 		32,
+
 	);
 
 	/**
diff --git src/wp-admin/includes/class-wp-site-logo.php src/wp-admin/includes/class-wp-site-logo.php
new file mode 100644
index 0000000..4950393
--- /dev/null
+++ src/wp-admin/includes/class-wp-site-logo.php
@@ -0,0 +1,162 @@
+<?php
+/**
+ * Administration API: WP_Site_Logo class
+ *
+ * @package WordPress
+ * @subpackage Administration
+ * @since 4.5.0
+ */
+
+/**
+ * Core class used to implement site logo functionality.
+ *
+ * @since 4.5.0
+ */
+class WP_Site_Logo {
+
+	/**
+	 * Get current logo settings stored in options.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 */
+	public function __construct() {
+		add_action( 'wp_head', array( $this, 'head_text_styles' ) );
+		add_action( 'delete_attachment', array( $this, 'delete_attachment_data' ) );
+		add_filter( 'image_size_names_choose', array( $this, 'media_manager_image_sizes' ) );
+	}
+
+	/**
+	 * Enqueue scripts for the Customizer live preview.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 */
+	public function preview_enqueue() {
+
+		// Don't bother passing in header text classes if the theme supports custom headers.
+		if ( ! current_theme_supports( 'custom-header' ) ) {
+			wp_enqueue_script( 'site-logo-header-text', plugins_url( '../js/site-logo-header-text.js', __FILE__ ), array( 'media-views' ), '', true );
+			wp_localize_script( 'site-logo-header-text', 'site_logo_header_classes', $this->header_text_classes() );
+		}
+	}
+
+	/**
+	 * Get header text classes. If not defined in add_theme_support(), defaults from Underscores will be used.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 *
+	 * @return string String of classes to hide
+	 */
+	public function header_text_classes() {
+		$args = get_theme_support( 'site-logo' );
+
+		if ( isset( $args[0]['header-text'] ) ) {
+			// Use any classes defined in add_theme_support().
+			$classes = $args[0]['header-text'];
+		} else {
+			// Otherwise, use these defaults, which will work with any Underscores-based theme.
+			$classes = array(
+				'site-title',
+				'site-description',
+			);
+		}
+
+		// If we've got an array, reduce them to a string for output.
+		if ( is_array( $classes ) ) {
+			$classes = array_map( 'sanitize_html_class', $classes );
+			$classes = (string) '.' . implode( ', .', $classes );
+		} else {
+			$classes = (string) '.' . $classes;
+		}
+
+		return $classes;
+	}
+
+	/**
+	 * Hide header text on front-end if necessary.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 */
+	public function head_text_styles() {
+		// Bail if our theme supports custom headers.
+		if ( current_theme_supports( 'custom-header' ) || get_theme_mod( 'site_logo_header_text', true ) ) {
+			return;
+		}
+
+		// Is Display Header Text unchecked? If so, we need to hide our header text.
+		?>
+		<!-- Site Logo: hide header text -->
+		<style type="text/css">
+			<?php echo sanitize_html_class( $this->header_text_classes() ); ?>  {
+				position: absolute;
+				clip: rect(1px, 1px, 1px, 1px);
+			}
+		</style>
+	<?php
+	}
+
+	/**
+	 * Make custom image sizes available to the media manager.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 *
+	 * @param array $sizes Image sizes.
+	 * @return array All default and registered custom image sizes.
+	 */
+	public function media_manager_image_sizes( $sizes ) {
+		// Get an array of all registered image sizes.
+		$intermediate = get_intermediate_image_sizes();
+
+		// Have we got anything fun to work with?
+		if ( is_array( $intermediate ) && ! empty( $intermediate ) ) {
+			foreach ( $intermediate as $key => $size ) {
+				// If the size isn't already in the $sizes array, add it.
+				if ( ! array_key_exists( $size, $sizes ) ) {
+					$sizes[ $size ] = $size;
+				}
+			}
+		}
+
+		return $sizes;
+	}
+
+	/**
+	 * Reset the site logo if the current logo is deleted in the media manager.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 *
+	 * @param int $post_id
+	 */
+	public function delete_attachment_data( $post_id ) {
+		$site_logo_id = get_option( 'site_logo' );
+
+		if ( $site_logo_id && $site_logo_id == $post_id ) {
+			delete_option( 'site_logo' );
+		}
+	}
+
+	/**
+	 * Sanitize our header text Customizer setting.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 *
+	 * @param int|string $input Input value.
+	 * @return int|string 1 if checked, empty string if not checked.
+	 */
+	public function sanitize_checkbox( $input ) {
+		return ( 1 == $input ) ? 1 : '';
+	}
+}
+
+/**
+ * WP_Site_Logo instance.
+ *
+ * @global WP_Site_Logo $wp_site_logo
+ */
+$GLOBALS['wp_site_logo'] = new WP_Site_Logo;
diff --git src/wp-admin/includes/template.php src/wp-admin/includes/template.php
index c658c6a..74d06ad 100644
--- src/wp-admin/includes/template.php
+++ src/wp-admin/includes/template.php
@@ -1748,13 +1748,17 @@ function _media_states( $post ) {
 		$media_states[] = __( 'Site Icon' );
 	}
 
+	if ( $post->ID == get_option( 'site_logo' ) ) {
+		$media_states[] = __( 'Logo' );
+	}
+
 	/**
 	 * Filter the default media display states for items in the Media list table.
 	 *
 	 * @since 3.2.0
 	 *
 	 * @param array $media_states An array of media states. Default 'Header Image',
-	 *                            'Background Image', 'Site Icon'.
+	 *                            'Background Image', 'Site Icon', 'Logo'.
 	 */
 	$media_states = apply_filters( 'display_media_states', $media_states );
 
diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
index 270770d..539b9bc 100644
--- src/wp-admin/js/customize-controls.js
+++ src/wp-admin/js/customize-controls.js
@@ -2296,6 +2296,43 @@
 	});
 
 	/**
+	 * A control for selecting Site Logos.
+	 *
+	 * @class
+	 * @augments wp.customize.MediaControl
+	 * @augments wp.customize.Control
+	 * @augments wp.customize.Class
+	 */
+	api.SiteLogoControl = api.MediaControl.extend({
+
+		/**
+		 * When the control's DOM structure is ready,
+		 * set up internal event bindings.
+		 */
+		ready: function() {
+			var control = this;
+
+			// Shortcut so that we don't have to use _.bind every time we add a callback.
+			_.bindAll( control, 'restoreDefault', 'removeFile', 'openFrame', 'select' );
+
+			// Bind events, with delegation to facilitate re-rendering.
+			control.container.on( 'click keydown', '.upload-button', control.openFrame );
+			control.container.on( 'click keydown', '.thumbnail-image img', control.openFrame );
+			control.container.on( 'click keydown', '.default-button', control.restoreDefault );
+			control.container.on( 'click keydown', '.remove-button', control.removeFile );
+
+			control.setting.bind( function( attachmentId ) {
+				wp.media.attachment( attachmentId ).fetch().done( function() {
+					wp.customize.previewer.send( 'site-logo-attachment-data', this.attributes );
+				} );
+
+				// Re-render whenever the control's setting changes.
+				control.renderContent();
+			} );
+		}
+	});
+
+	/**
 	 * @class
 	 * @augments wp.customize.Control
 	 * @augments wp.customize.Class
@@ -3201,6 +3238,7 @@
 		image:         api.ImageControl,
 		cropped_image: api.CroppedImageControl,
 		site_icon:     api.SiteIconControl,
+		site_logo:     api.SiteLogoControl,
 		header:        api.HeaderControl,
 		background:    api.BackgroundControl,
 		theme:         api.ThemeControl
diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
index 937689b..c0e4fc2 100644
--- src/wp-includes/class-wp-customize-manager.php
+++ src/wp-includes/class-wp-customize-manager.php
@@ -217,6 +217,7 @@ final class WP_Customize_Manager {
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php' );
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php' );
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' );
+		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-logo-control.php' );
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php' );
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php' );
 		require_once( ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php' );
@@ -1857,6 +1858,7 @@ final class WP_Customize_Manager {
 		$this->register_control_type( 'WP_Customize_Background_Image_Control' );
 		$this->register_control_type( 'WP_Customize_Cropped_Image_Control' );
 		$this->register_control_type( 'WP_Customize_Site_Icon_Control' );
+		$this->register_control_type( 'WP_Customize_Site_Logo_Control' );
 		$this->register_control_type( 'WP_Customize_Theme_Control' );
 
 		/* Themes */
@@ -1932,6 +1934,23 @@ final class WP_Customize_Manager {
 			'section'    => 'title_tagline',
 		) );
 
+		// Add a setting to hide header text if the theme isn't supporting the feature itself.
+		// @todo
+		if ( ! current_theme_supports( 'custom-header' ) ) {
+			$this->add_setting( 'header_text', array(
+				'default'           => 1,
+				'sanitize_callback' => 'absint',
+				'transport'         => 'postMessage',
+			) );
+
+			$this->add_control( 'header_text', array(
+				'label'    => __( 'Display Site Title and Tagline' ),
+				'section'  => 'title_tagline',
+				'settings' => 'header_text',
+				'type'     => 'checkbox',
+			) );
+		}
+
 		$this->add_setting( 'site_icon', array(
 			'type'       => 'option',
 			'capability' => 'manage_options',
@@ -1951,6 +1970,28 @@ final class WP_Customize_Manager {
 			'width'       => 512,
 		) ) );
 
+		$this->add_setting( 'site_logo', array(
+			'theme_supports' => array( 'site-logo' ),
+			'type'           => 'option',
+			'capability'     => 'manage_options',
+			'transport'      => 'postMessage',
+		) );
+
+		$this->add_control( new WP_Customize_Site_Logo_Control( $this, 'site_logo', array(
+			'label'    => __( 'Logo' ),
+			'section'  => 'title_tagline',
+			'priority' => 0,
+		) ) );
+
+		if ( isset( $this->selective_refresh ) ) {
+			$this->selective_refresh->add_partial( 'site_logo', array(
+				'settings'            => array( 'site_logo' ),
+				'selector'            => '.site-logo-link',
+				'render_callback'     => array( $this, '_render_site_logo_partial' ),
+				'container_inclusive' => true,
+			) );
+		}
+
 		/* Colors */
 
 		$this->add_section( 'colors', array(
@@ -2180,6 +2221,24 @@ final class WP_Customize_Manager {
 
 		return $color;
 	}
+
+	/**
+	 * Callback for rendering the site logo, used in the site_logo partial.
+	 *
+	 * This method exists because the partial object and context data are passed
+	 * into a partial's render_callback so we cannot use get_the_site_logo() as
+	 * the render_callback directly since it expects a blog ID as the first
+	 * argument. When WP no longer supports PHP 5.3, this method can be removed
+	 * in favor of an anonymous function.
+	 *
+	 * @see WP_Customize_Manager::register_controls()
+	 *
+	 * @since 4.5.0
+	 * @access private
+	 */
+	public function _render_site_logo_partial() {
+		return get_the_site_logo();
+	}
 }
 
 /**
diff --git src/wp-includes/customize/class-wp-customize-site-icon-control.php src/wp-includes/customize/class-wp-customize-site-icon-control.php
index fe8ac37..4d20f8f 100644
--- src/wp-includes/customize/class-wp-customize-site-icon-control.php
+++ src/wp-includes/customize/class-wp-customize-site-icon-control.php
@@ -41,4 +41,71 @@ class WP_Customize_Site_Icon_Control extends WP_Customize_Cropped_Image_Control
 		parent::__construct( $manager, $id, $args );
 		add_action( 'customize_controls_print_styles', 'wp_site_icon', 99 );
 	}
+
+	/**
+	 * Render a JS template for the content of the site icon control.
+	 *
+	 * @since 4.5.0
+	 */
+	public function content_template() {
+		?>
+		<label for="{{ data.settings['default'] }}-button">
+			<# if ( data.label ) { #>
+				<span class="customize-control-title">{{ data.label }}</span>
+			<# } #>
+			<# if ( data.description ) { #>
+				<span class="description customize-control-description">{{{ data.description }}}</span>
+			<# } #>
+		</label>
+
+		<# if ( data.attachment && data.attachment.id ) { #>
+		<div class="current">
+			<div class="container">
+				<div class="attachment-media-view attachment-media-view-{{ data.attachment.type }} {{ data.attachment.orientation }} site-icon-preview">
+					<strong><?php _e( 'As a browser icon' ); ?></strong>
+					<div class="favicon-preview">
+						<img src="images/browser.png" class="browser-preview" width="182" height="" alt="" />
+
+						<div class="favicon">
+							<img id="preview-favicon" src="{{ data.attachment.sizes.full.url }}" alt="<?php esc_attr_e( 'Preview as a browser icon' ); ?>"/>
+						</div>
+						<span class="browser-title"><?php bloginfo( 'name' ); ?></span>
+					</div>
+
+					<strong><?php _e( 'As an app icon' ); ?></strong>
+					<p class="app-icon-preview">
+						<img id="preview-app-icon" src="{{ data.attachment.sizes.full.url }}" alt="<?php esc_attr_e( 'Preview as an app icon' ); ?>"/>
+					</p>
+				</div>
+			</div>
+		</div>
+		<div class="actions">
+			<# if ( data.canUpload ) { #>
+				<button type="button" class="button remove-button"><?php echo $this->button_labels['remove']; ?></button>
+				<button type="button" class="button upload-button" id="{{ data.settings['default'] }}-button"><?php echo $this->button_labels['change']; ?></button>
+				<div style="clear:both"></div>
+			<# } #>
+		</div>
+		<# } else { #>
+		<div class="current">
+			<div class="container">
+				<div class="placeholder">
+					<div class="inner">
+						<span><?php echo $this->button_labels['placeholder']; ?></span>
+					</div>
+				</div>
+			</div>
+		</div>
+		<div class="actions">
+			<# if ( data.defaultAttachment ) { #>
+				<button type="button" class="button default-button"><?php echo $this->button_labels['default']; ?></button>
+			<# } #>
+			<# if ( data.canUpload ) { #>
+				<button type="button" class="button upload-button" id="{{ data.settings['default'] }}-button"><?php echo $this->button_labels['select']; ?></button>
+			<# } #>
+			<div style="clear:both"></div>
+		</div>
+		<# } #>
+		<?php
+	}
 }
diff --git src/wp-includes/customize/class-wp-customize-site-logo-control.php src/wp-includes/customize/class-wp-customize-site-logo-control.php
new file mode 100644
index 0000000..1ad14a8
--- /dev/null
+++ src/wp-includes/customize/class-wp-customize-site-logo-control.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Customize API: WP_Customize_Site_Logo_Control class
+ *
+ * @package WordPress
+ * @subpackage Customize
+ * @since 4.5.0
+ */
+
+/**
+ * Customize Site Logo control class.
+ *
+ * Used only for custom functionality in JavaScript.
+ *
+ * @since 4.5.0
+ *
+ * @see WP_Customize_Image_Control
+ */
+class WP_Customize_Site_Logo_Control extends WP_Customize_Image_Control {
+
+	/**
+	 * Control type.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 * @var string
+	 */
+	public $type = 'site_logo';
+
+	/**
+	 * Constructor.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 *
+	 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
+	 * @param string               $id      Control ID.
+	 * @param array                $args    Optional. Arguments to override class property defaults.
+	 */
+	public function __construct( $manager, $id, $args = array() ) {
+		parent::__construct( $manager, $id, $args );
+
+		$this->button_labels = array(
+			'select'       => __( 'Select logo' ),
+			'change'       => __( 'Change logo' ),
+			'remove'       => __( 'Remove' ),
+			'default'      => __( 'Default' ),
+			'placeholder'  => __( 'No logo selected' ),
+			'frame_title'  => __( 'Select logo' ),
+			'frame_button' => __( 'Choose logo' ),
+		);
+	}
+}
diff --git src/wp-includes/general-template.php src/wp-includes/general-template.php
index 1669ae3..27ab304 100644
--- src/wp-includes/general-template.php
+++ src/wp-includes/general-template.php
@@ -836,6 +836,93 @@ function has_site_icon( $blog_id = 0 ) {
 }
 
 /**
+ * Whether the site has a Site Logo.
+ *
+ * @since 4.5.0
+ *
+ * @param int $blog_id Optional. ID of the blog in question. Default current blog.
+ * @return bool Whether the site has a site logo or not.
+ */
+function has_site_logo( $blog_id = 0 ) {
+	if ( is_multisite() && (int) $blog_id !== get_current_blog_id() ) {
+		switch_to_blog( $blog_id );
+	}
+
+	$site_logo_id = get_option( 'site_logo' );
+
+	if ( is_multisite() && ms_is_switched() ) {
+		restore_current_blog();
+	}
+
+	return (bool) $site_logo_id;
+}
+
+/**
+ * Returns a Site Logo, linked to home.
+ *
+ * @since 4.5.0
+ *
+ * @param int $blog_id Optional. ID of the blog in question. Default current blog.
+ * @return string Site logo markup.
+ */
+function get_the_site_logo( $blog_id = 0 ) {
+	$html = '';
+
+	if ( is_multisite() && (int) $blog_id !== get_current_blog_id() ) {
+		switch_to_blog( $blog_id );
+	}
+
+	$site_logo_id = get_option( 'site_logo' );
+
+	if ( is_multisite() && ms_is_switched() ) {
+		restore_current_blog();
+	}
+	$size = get_theme_support( 'site-logo' );
+	$size = $size[0]['size'];
+
+	// We have a logo. Logo is go.
+	if ( $site_logo_id ) {
+		$html = sprintf( '<a href="%1$s" class="site-logo-link" rel="home" itemprop="url">%2$s</a>',
+			esc_url( home_url( '/' ) ),
+			wp_get_attachment_image( $site_logo_id, $size, false, array(
+				'class'     => "site-logo attachment-$size",
+				'data-size' => $size,
+				'itemprop'  => 'logo',
+			) )
+		);
+	}
+
+	// If no logo is set but we're in the Customizer, leave a placeholder (needed for the live preview).
+	elseif ( is_customize_preview() ) {
+		$html = sprintf( '<a href="%1$s" class="site-logo-link" style="display:none;"><img class="site-logo" data-size="%2$s" /></a>',
+			esc_url( home_url( '/' ) ),
+			esc_attr( $size )
+		);
+	}
+
+	/**
+	 * Filter the Site Logo output.
+	 *
+	 * @since 4.5.0
+	 *
+	 * @param string $html Site Logo HTML output.
+	 * @param string $size Size specified in add_theme_support declaration, or 'thumbnail' default.
+	 */
+	return apply_filters( 'get_the_site_logo', $html, $size );
+}
+
+/**
+ * Displays a Site Logo, linked to home.
+ *
+ * @since 4.5.0
+ *
+ * @param int $blog_id Optional. ID of the blog in question. Default current blog.
+ */
+function the_site_logo( $blog_id = 0 ) {
+	echo get_the_site_logo( $blog_id );
+}
+
+/**
  * Returns document title for the current page.
  *
  * @since 4.4.0
diff --git src/wp-includes/js/customize-preview.js src/wp-includes/js/customize-preview.js
index b6da775..fd3c3ae 100644
--- src/wp-includes/js/customize-preview.js
+++ src/wp-includes/js/customize-preview.js
@@ -223,6 +223,62 @@
 			});
 		});
 
+		/**
+		 * Site Logo
+		 *
+		 * The site logo setting only contains the attachment ID. To avoid having to send an AJAX request to get more
+		 * data, we send a separate message with the attachment data we get from the Customizer's media modal.
+		 * Therefore first callback handles only the event of a new logo being selected.
+		 *
+		 * We don't need any information about a removed logo, so the second callback only handles that.
+		 *
+		 * @since 4.5.0
+		 */
+		api.preview.bind( 'site-logo-attachment-data', function( attachment ) {
+			var $logo  = $( '.site-logo' ),
+				size   = $logo.data( 'size' ),
+				srcset = [];
+
+			// If the source was smaller than the size required by the theme, give the biggest we've got.
+			if ( ! attachment.sizes[ size ] ) {
+				size = 'full';
+			}
+
+			_.each( attachment.sizes, function( size ) {
+				srcset.push( size.url + ' ' + size.width + 'w' );
+			} );
+
+			$logo.attr( {
+				height: attachment.sizes[ size ].height,
+				width:  attachment.sizes[ size ].width,
+				src:    attachment.sizes[ size ].url,
+				srcset: srcset
+			} );
+
+			$( '.site-logo-link' ).show();
+			$( 'body' ).addClass( 'wp-site-logo' );
+		} );
+
+		api( 'site_logo', function( setting ) {
+			setting.bind( function( newValue ) {
+				if ( ! newValue ) {
+					$( '.site-logo-link' ).hide();
+					$( 'body' ).removeClass( 'wp-site-logo' );
+				}
+			} );
+
+			// Focus on the control when the logo is clicked, if there is no site_logo partial.
+			if ( ! api.partial || ! api.partial.has( 'site_logo' ) ) {
+				$( document.body ).on( 'click', '.site-logo-link', function( e ) {
+					if ( ! e.shiftKey ) {
+						return;
+					}
+					api.preview.send( 'focus-control-for-setting', 'site_logo' );
+				} );
+				$( '.site-logo-link' ).attr( 'title', 'Shift-click to edit the site logo.' );
+			}
+		} );
+
 		api.trigger( 'preview-ready' );
 	});
 
diff --git src/wp-includes/post-template.php src/wp-includes/post-template.php
index 5ceed59..6d788ff 100644
--- src/wp-includes/post-template.php
+++ src/wp-includes/post-template.php
@@ -706,6 +706,10 @@ function get_body_class( $class = '' ) {
 	if ( get_background_color() !== get_theme_support( 'custom-background', 'default-color' ) || get_background_image() )
 		$classes[] = 'custom-background';
 
+	if ( has_site_logo() ) {
+		$classes[] = 'wp-site-logo';
+	}
+
 	$page = $wp_query->get( 'page' );
 
 	if ( ! $page || $page < 2 )
