I have been experimenting with overlaying a div on top of the iframe used for the YouTube video player. So far, I have come up with a method of showing a high quality preview instead of the crappy thumbnail preview image that YouTube generates for my videos. There are still some glitches and I have not tested this on anything but Chrome using Windows 7. Click on the video below and it should start playing. The preview image is a completely separate JPEG file in a separate div from the YouTube player iframe. The play button is in another div on top of that. I could have used a background image but then the preview image could not be scaled to fit in the div.

YouTube Embedded Player with High Quality Preview Image

Below is a typical embedded YouTube player. This is to show the contrast between my new preview image feature and the typical YouTube video preview image. Of course YouTube might come along and improve things so this is only a crappy preview image as of this posting. After that is a screen shot of the crappy preview image taken while I’m writing this post to show what I am working with at this time. Note that the image has lines along the edges and some JPEG artifacts are look fairly ugly.

Typical YouTube Embedded Player


Screen Capture Of YouTube Player Preview Image

The code below is the PHP file that handles creation of the embedded player and creates the overlay div. I used PHP in an iframe of my own making to keep each instance of the embedded player and overlay separate when there is more than one on a page. I also wanted to use this in my blog and this is the easiest way to do so.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html lang="en" style="margin: 0; padding: 0; border: 0;">
		<script src = "http://www.youtube.com/player_api"></script>
	<body style="margin: 0; padding: 0; border: 0;">
			if( isset( $_GET['video'] ) )
				$Video = $_GET['video'];
				$Width = 672;
				if( isset( $_GET['width'] ) )
					$Width = $_GET['width'];
				$Height = 378;
				if( isset( $_GET['height'] ) )
					$Height = $_GET['height'];

				print( '<iframe id="player" onload="floaded()" seamless="seamless" frameBorder="0" style="position: absolute; top: 0; left: 0; z-index:1; margin: 0; padding: 0; border: 0;" ' );
				print( 'width="' . $Width . '" height="' . $Height . '" ');
				print( 'frameborder="0" src="http://www.youtube.com/embed/' . $Video );
				print( '?enablejsapi=1&wmode=opaque&showinfo=0&noinfo=1&rel=0&vq=large&modestbranding=0&autohide=1&controls=1&color=red&theme=light&autohide=1">' );
				print( '</iframe>' . "\n" );
				$Preview = "http://i.ytimg.com/vi/" . $Video . "/maxresdefault.jpg";
				$PreviewAdjust = 1;
				if( isset( $_GET['preview'] ) )
					$Preview = $_GET['preview'];
					$PreviewAdjust = 0;
				print( '<div id="theoverlaydivforvideo" onclick="startit()" style="cursor: pointer; position: absolute; top: 0; left: 0; margin: 0; padding: 0; border: 0; color: white; opacity:1; z-index:50; ' );
				print( 'width: ' . $Width . 'px; height:' . $Height . 'px; ' );
				//print( "background: center #fff url( '" . $Preview . "' );" );
				print( '">' );
				print( '<div style="position: absolute; top: 0; left: 0; overflow: hidden; margin: 0; padding: 0; border: 0; width: ' . $Width . 'px; height:' . $Height . 'px;" >' );
				print( '<img style="margin-left: -' . $PreviewAdjust . 'px; margin-top: -' . $PreviewAdjust . 'px; width: ' . ( $Width + $PreviewAdjust + $PreviewAdjust ) . 'px; height:' . ( $Height + $PreviewAdjust + $PreviewAdjust ). 'px;" src="' . $Preview . '"/>' );
				print( '</div>' );
				print( '<div style="position: absolute; top: 0; left: 0; margin: 0; padding: 0; border: 0; width: ' . $Width . 'px; height:' . $Height . 'px;" >' );
				print( '<table style="width: ' . $Width . 'px; height:' . $Height . 'px; "><tr>' );
				print( '<td style="text-align:center; vertical-align:middle; overflow: hidden; border: 0; margin: 0; padding: 0;">' );
				print( '<img style="opacity:.8" src="http://www.rectorsquid.com/Play_Button.png"/>' );
				print( '</td></tr></table></div>' . "\n" );
				print( '</div>' );

		<script type="text/javascript" >

			var player;

			function HideOverlay()
				var Preview = document.getElementById( "theoverlaydivforvideo" );
				if( Preview )
					Preview.style.display = "none";

			function startit()
				window.setTimeout( HideOverlay, 250 );

			function floaded()
				player = new YT.Player('player', 
						print( "videoId: '" . $Video . "',\n" );
					{  'onStateChange': function (event)
if (event.data == YT.PlayerState.ENDED )
						{  alert("Ended"); }
else if (event.data == YT.PlayerState.PLAYING )
						{  alert("Playing"); }
else if (event.data == YT.PlayerState.PAUSED )
						{  alert("Paused"); }
else if (event.data == YT.PlayerState.BUFFERING )
						{  alert("Buffering"); }
else if (event.data == YT.PlayerState.CUED )
						{  alert("Cued"); }




I did some interesting things in the code. One is to let the caller pass in a preview image URL if needed but to default to using the YouTube high quality preview image otherwise. The YouTube preview image is at a very specific location and that could change in the future. Hopefully that won’t happen since my code does nothing to detect if the file is missing. Another interesting thing is that I sort of crop the YouTube high quality image if it is used. I do this because of those artifact lines on the sides of the image. A simple two pixel size adjustment in each direction causes those artifacts to be hidden.

I’m still playing with this code so there may be some coding mistakes and some script code is commented out that I am thinking about using in the future. I see no reason why I can’t show my own buffering animated GIF while the video is buffering and then hide the overlay once the player changes to a playing state. The state change code will then become useful.

Below is the video player overlaid with the high quality YouTube preview image adjusted to hide the artifacts.

Using YouTube High Quality Thumbnail Image

Anyone who finds this post is free to use this code however they see fit.