Skip to main content
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: John Hann
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: John Hann@johnmhann )

Canvas "alphabetic" textBaseline Is Consistent Across Browsers

By on

Earlier this week, I took a look at rendering text to <canvas> using an adjusted X,Y offset for cross-browser consistency. In that demo, I was using a textBaseline of top, which is inherently different from browser to browser. After I posted that, Jan Sedivy - creator of InVision Freehand - told me that while top may be inconsistent, a textBaseline of alphabetic is rendered perfectly across all browsers. And, in fact, also matches the baseline rendering of the browser's native text (outside of Canvas). As such, I wanted to perform a fast-follow demo to look at how the alphabetic baseline renders in Chrome, Firefox, and Safari.

Run this demo in my JavaScript Demos project on GitHub.

View this code in my JavaScript Demos project on GitHub.

Just as with my previous demo, I'm going to first render a grid of lines to the Canvas element so that we can clearly see where text is being rendered. Then, I'm going to render sample text at various pixel-sizes so that we can see the relationship between the font-sizes and the rendering:

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1" />
	<title>
		Canvas "alphabetic" textBaseline Is Consistent Across Browsers
	</title>
	<link rel="preconnect" href="https://fonts.googleapis.com" />
	<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
	<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap" />
	<style type="text/css">
		body {
			font-family: "Roboto", sans-serif ;
		}
		canvas {
			box-shadow: 0px 0px 0px 2px #000000 ;
		}
	</style>
</head>
<body>

	<h1>
		Canvas "alphabetic" textBaseline Is Consistent Across Browsers
	</h1>

	<canvas id="demo" width="400" height="400"></canvas>

	<script type="text/javascript">

		var canvas = document.querySelector( "#demo" );
		var canvasWidth = 400;
		var canvasHeight = 400;
		var context = canvas.getContext( "2d" );

		// We have to give the FONT time to load so that we can use it on the canvas.
		window.addEventListener(
			"load",
			() => {

				drawGridLines();
				drawText( "Nobody puts baby in a corner!" );

			}
		);

		// --------------------------------------------------------------------------- //
		// --------------------------------------------------------------------------- //

		/**
		* I draw the sample text to canvas at various sizes.
		*/
		function drawText( textValue ) {

			var pairings = [
				{ fontSize: 10, y: 50 },
				{ fontSize: 20, y: 100 },
				{ fontSize: 30, y: 150 },
				{ fontSize: 40, y: 200 },
				{ fontSize: 50, y: 250 },
				{ fontSize: 60, y: 300 },
				{ fontSize: 70, y: 350 }
			];

			context.fillStyle = "#000000";
			context.textBaseline = "alphabetic";

			for ( var pairing of pairings ) {

				context.font = `500 ${ pairing.fontSize }px Roboto`;
				context.fillText( textValue, 50, pairing.y );

			}

		}


		/**
		* I draw the horizontal and vertical grid lines on the canvas so that we can more
		* easily see where the text is aligned on different browsers.
		*/
		function drawGridLines() {

			var step = 10;
			var jump = 50;

			context.lineWidth = 1;
			context.strokeStyle = "#cccccc";

			// Draw GREY horizontal grid lines.
			for ( var i = step ; i < canvasHeight ; i += step ) {

				context.beginPath();
				context.moveTo( 0, ( i - 0.5 ) );
				context.lineTo( canvasWidth, ( i - 0.5 ) );
				context.stroke();

			}
			// Draw GREY vertical grid lines.
			for ( var i = step ; i < canvasWidth ; i += step ) {

				context.beginPath();
				context.moveTo( ( i - 0.5 ), 0 );
				context.lineTo( ( i - 0.5 ), canvasHeight );
				context.stroke();

			}

			context.strokeStyle = "#ff3333";

			// Draw RED horizontal grid lines.
			for ( var i = jump ; i < canvasHeight ; i += jump ) {

				context.beginPath();
				context.moveTo( 0, ( i - 0.5 ) );
				context.lineTo( canvasWidth, ( i - 0.5 ) );
				context.stroke();

			}
			// Draw RED vertical grid lines.
			for ( var i = jump ; i < canvasWidth ; i += jump ) {

				context.beginPath();
				context.moveTo( ( i - 0.5 ), 0 );
				context.lineTo( ( i - 0.5 ), canvasHeight );
				context.stroke();

			}

		}

	</script>

</body>
</html>

And, when run this is Chrome, Firefox, and Safari, we get the following output:

As you can see, using a textBaseline of alphabetic looks more-or-less exactly the same in each browser. But, the big difference, rendering-wise, between this demo and the previous demo is that the text is being rendered above the red lines as opposed to below the red lines. In order to account for that, we'd have to adjust the location of the y coordinate in the .fillText() invocation - but that's a post for another day.

Want to use code from this post? Check out the license.

Reader Comments

Post A Comment — I'd Love To Hear From You!

Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.