Pixel walker

With a good dose of irony I have a soft spot for pixel art. And yet, even crisp vector graphics are suited to the specific style. Especially if you explore the markup language and tolerate a few manual adjustments, not to mention a considerable degree of repetition.

Setup

In the viewBox attribute you find what is likely the most ingenious part of using SVG. The moment you draw in increments, unit by unit, all you need in terms of dimensions is the right number of columns, the smallest number of rows.

<svg viewBox="0 0 8 8">
	<!-- 8-bit -->
</svg>

Rectangles

In the <svg> element, the most immediate way to draw pixelated pictures goes through the <rect> element. There is nothing sophisticated about the approach: add a rectangle at precise coordinates with a width and height of 1.

<rect x="3" width="1" height="1" />
<rect y="1" width="1" height="1" />
<rect x="6" y="1" width="1" height="1" />
<rect x="2" y="2" width="3" height="1" />

The moment you have neighboring cells you can save a few keystrokes and stretch the shape horizontally, but that’s it.

Sun

Pixels

It is certainly a hassle to repeat the <rect> element. And it may not seem like it, but the duplication of the width and height attributes adds up. To try and reduce the issue, define a square early.

<defs>
	<rect id="p" width="1" height="1" />
</defs>

One pixel you can then repeat with the <use> element.

<use href="#p" x="3" y="1" />
<use href="#p" x="3" y="2" />
<use href="#p" y="3" />
<use href="#p" x="1" y="3" />

Admittedly, the gains are marginal. You may save a handful of characters here and there, but also lose the flexibility of wider rectangles. That being said, there is something conceptually nice with the format. You feel as an artist dotting the canvas into an intentional work of art.

Star

Lines

Moving on to a less pretentious, but more practical approach, the <path> element promises a lot with a wickedly simple idea: draw a line 1 unit wide.

<g fill="none" stroke="currentColor" stroke-width="1">
	<path d="..." />
</g>

Technically, you need to set only the stroke’s color as the thickness is 1 by default. The fill is also unnecessary, and you’ll soon appreciate why.

<g stroke="currentColor">
	<path d="..." />
</g>

In the dattribute the instructions are then imperative and clear: start at a precise (x, y) coordinate.

<path d="M 1 1" />

Follow up with a horizontal rule.

<path d="M 1 1 h 4" />

And, repeat. Letter M origin, small h offset.

<path d="M 1 1 h 4 M 2 2 h 4 M 3 3 h 4" />

If you are maniacally attached to brevity you can remove most whitespace. You just need to separate the numbers which would otherwise mingle and confound the browser.

<path d="M1 1h4M2 2h4M3 3h4" />

With or without the change, however, the solution puts the previous sections to shame.

There is one, outstanding issue, evident as you sketch out the rows.

Off by 0.5

As the stroke of <path> elements is drawn from the center, the line is drawn at the precise y offset, but half above, half below.

To compensate, knock the line down by exactly half the stroke’s width.

<g transform="translate(0 0.5)">
	<!-- ...path -->
</g>

If you fancy the viewBox attribute you can change the origin of the entire visual.

<svg viewBox="0 -0.5 8 8">
	<g stroke="currentColor">
		<!-- ...path -->
	</g>
</svg>

But once you apply the translation, you are then free to complete the picture bit by bit.

Moon

Layers

Drawing more complex sprites is a matter of expanding the viewBox, and indubitably, a matter of patiently curating the d attribute in the larger canvas.

<svg viewBox="0 -0.5 16 16">
	<g stroke="currentColor">
		<path d="M .." />
	</g>
</svg>

For more elaborate compositions, then, multiply the effort with additional <path> elements.

<g stroke="hsl(0, 0%, 10%)">
	<path d="M 5 0 h 6 ..." />
</g>
<g stroke="hsl(0, 0%, 50%)">
	<path d="M 5 5 h 2 ..." />
</g>

HSL helps to distinguish between layers just by changing the lightness value, but a previous article inspires more freedom through CSS custom properties. Pick a convention to name the different levels.

<g stroke="var(--c0, hsl(0, 0%, 10%))">
	<!-- ...path -->
</g>
<g stroke="var(--c1, hsl(0, 0%, 50%))">
	<!-- ...path -->
</g>

And experiment with different styles by changing the properties.

svg {
	--c0: hsl(0, 0%, 14%);
	--c1: hsl(123, 30%, 47%);
	--c2: hsl(60, 33%, 99%);
}

Care to guess the drawing I’ve conjured up this time with the three colored layers?

Boy

Sprites

It should be evident I developed a preference for <path> elements, and that I’ve also indulged in another role-playing game from the Game Boy era, tracing the roots of the Sword of Mana series.

Parenthesis aside, the detour on <use> elements comes back to help to create a full-blown spritesheet.

Consider the sprites as independent units. With this in mind, encapsulate the drawing logic in <symbol> elements.

<symbol id="d0" viewBox="0 -0.5 16 16">
	<!-- ...gs -->
</symbol>
<symbol id="d1" viewBox="0 -0.5 16 16">
	<!-- ...gs -->
</symbol>

A symbol works similarly to the larger SVG in that you are able to define a canvas through the viewBox attribute; in between the tags, the elements will follow the specific rule.

Unlike the wrapping <svg>, however, nothing is rendered on screen. Not even outside of the safe, defining boundaries of the <defs> element.

<svg>
	<symbol id="d0" viewBox="0 -0.5 16 16">
		<!-- ...gs  -->
	</symbol>
</svg>

To show the sprite back, as thinly referenced as it may have been, point to the sprite through the <use> element.

<use href="#d0" />

Not only you are able to inject the symbol in one concise declaration. Through the widthand height attributes you can resize the image to fit in the scope of the SVG at hand.

For the sake of argument, you can set up a canvas as an exceedingly small rectangle.

<svg viewBox="0 0 2 1">
	<!-- ...symbols -->
</svg>

And show multiple sprites with <use> elements 1 unit wide and tall.

<svg viewBox="0 0 2 1">
	<!-- ...symbols -->
	<use href="#d0" width="1" height="1" />
	<use href="#d1" x="1" width="1" height="1" />
</svg>

Enough to prove you did take the time to draw a slightly different version to create the illusion of movement.

Boys

Animation

It would be cruel to mention movement and not explore the concept with an animation or two.

A first animation leads to a much better way to show the two sprites, in succession instead of side by side.

Resize the canvas to show just one character.

<svg viewBox="0 0 1 1">
	<!-- ...symbols -->
	<use href="#d0" width="1" height="1" />
</svg>

Instead of closing the <use> element immediately, add the <animate> element between the opening and closing tags.

<use href="#d0" width="1" height="1">
	<animate />
</use>

With SMIL animation and the element in particular you are able to update whichever attribute, including the reference to the spritesheet.

<use href="#d0" width="1" height="1">
	<animate 
		attributeName="href" 
		values="#d0; #d1" 
		dur="0.4s"
	/>
</use>

Theoretically, the calcMode attribute describes how to interpolate between values, and if you were to be particular you could detail a value of discrete.

<animate 
	... 
	calcMode="discrete"
/>

But the browser is smart enough to move from reference to reference without much second guessing.

All you need is to admire the result. And if you really wanted to show off, add the begin attribute so that the animation plays out whenever you choose to tap the screen.

<animate 
	... 
	begin="click" 
	restart="whenNotActive"
/>

Do you have enough energy left for a few steps?

Stepping boy

There are different ways to animate sprites, and you could achieve a similar effect just with CSS. You could keep the two sprites side by side, only show one and rapidly move between the images. As if flipping through barely different pages.

That being said, SMIL animation has plenty more to offer, including synchronized animations. The hope is that this final demo will convince you of that.

Game boy

Care to guess why our boy is finally off center, seemingly lost in whitespace? Once again, the answer might be a tap, and a few steps, away.