<main class="Container">
        <img class="TitleImage PixelArtImage" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-WalkingSprites.png" alt="Walking Demo Sprites" />
        <div class="SpritesheetSlider">
           
           
            <!-- This div is our character -->
            <div class="Character Character--walk-down">
                <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-Shadow.png" class="Character_shadow PixelArtImage" />
                <!-- Spritesheets injected here: -->
            </div>
           
           
           
           
            <div class="Navigation flex-center">
                <!-- Navigation bubbles injected here: -->
            </div>
            <button class="NextSpritesheetButton NextSpritesheetButton--prev" onclick="setPreviousActive()">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 4 7" shape-rendering="crispEdges">
                    <metadata>Made with Pixels to Svg https://codepen.io/shshaw/pen/XbxvNj</metadata>
                    <path stroke="#434343" d="M3 0h1M2 1h1M1 2h1M0 3h1M1 4h1M2 5h1M3 6h1" />
                </svg>
            </button>
            <button class="NextSpritesheetButton NextSpritesheetButton--next" onclick="setNextActive()">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 4 7" shape-rendering="crispEdges">
                    <metadata>Made with Pixels to Svg https://codepen.io/shshaw/pen/XbxvNj</metadata>
                    <path stroke="#434343" d="M0 0h1M1 1h1M2 2h1M3 3h1M2 4h1M1 5h1M0 6h1" />
                </svg>
            </button>
        </div>
        <div class="DemoDirectionUI flex-center">
            <button class="DirectionArrow DirectionArrow-left" onclick="setDirection('LEFT')">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
                    <path class="Arrow_outline-top"  stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
                    <path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h5M7 3h5M1 4h4M7 4h5M1 5h3M7 5h5M1 6h4M7 6h5M1 7h5M7 7h5M1 8h11" />
                    <path class="Arrow_arrow-inset"  stroke="#434343" d="M6 3h1M5 4h1M4 5h1" />
                    <path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 4h1M5 5h2M5 6h2M6 7h1" />
                    <path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
                    <path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
                    <path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
                </svg>
            </button>
            <button class="DirectionArrow DirectionArrow-up" onclick="setDirection('UP')">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
                    <path class="Arrow_outline-top"  stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
                    <path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h11M1 4h5M7 4h5M1 5h4M8 5h4M1 6h3M9 6h3M1 7h11M1 8h11" />
                    <path class="Arrow_arrow-inset"  stroke="#434343" d="M6 4h1M5 5h1M7 5h1" />
                    <path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 5h1M4 6h5" />
                    <path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
                    <path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
                    <path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
                </svg>
            </button>
            <button class="DirectionArrow DirectionArrow-down DirectionArrow--active" onclick="setDirection('DOWN')">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
                    <path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
                    <path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h11M1 4h3M9 4h3M1 5h4M8 5h4M1 6h5M7 6h5M1 7h11M1 8h11" />
                    <path class="Arrow_arrow-inset" stroke="#434343" d="M4 4h5" />
                    <path class="Arrow_arrow-body" stroke="#5f5f5f" d="M5 5h3M6 6h1" />
                    <path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
                    <path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
                    <path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
                </svg>
            </button>
            <button class="DirectionArrow DirectionArrow-right" onclick="setDirection('RIGHT')">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
                    <path class="Arrow_outline-top"  stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
                    <path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h5M7 3h5M1 4h5M8 4h4M1 5h5M9 5h3M1 6h5M8 6h4M1 7h5M7 7h5M1 8h11" />
                    <path class="Arrow_arrow-inset"  stroke="#434343" d="M6 3h1M7 4h1M8 5h1" />
                    <path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 4h1M6 5h2M6 6h2M6 7h1" />
                    <path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
                    <path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
                    <path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
                </svg>
            </button>
        </div>
    </main>
    <footer>
    	<p>Characters from <a href="https://store.steampowered.com/app/1064690/Danger_Crew?icid=codepenwalkingdemo" target="_blank">Danger Crew</a>, an RPG about being a programmer!
    </footer>
/* 
The comments here attempt to explain the implementations of spritesheet animations in Danger Crew.

We'll utilize a dynamic "pixel size" which will allow us to upscale the pixel art in even multiplications.
EX: if an asset is designed as a 10px by 10px square, we can multiply by pixel size of 4 to enlarge the asset to 40px by 40px. This value can be adjusted per screen real estate. 
*/

:root {
  --pixel-size: 4; /* Try playing with this value! */
}


/* 
   Allow pixel art images and canvas tags to scale up without losing quality
*/
.PixelArtImage {
  	image-rendering: -moz-crisp-edges;
  	image-rendering: -webkit-crisp-edges;
  	image-rendering: pixelated;
  	image-rendering: crisp-edges;
}


/* 
	This is the square crop of the character, each frame designed at natural size 32x32. 
	Multiplied by dynamic pixel size.
*/
.Character {
	width: calc(var(--pixel-size) * 32px);
	height: calc(var(--pixel-size) * 32px);
	overflow: hidden;
	position: relative;
	margin: calc(var(--pixel-size) * 6px) auto;
}
.Character_shadow {
	width: calc(var(--pixel-size) * 32px);
	height: calc(var(--pixel-size) * 32px);
	opacity: 0.25;
	position: absolute;
}

/* Set up a CSS Animation to move our spritesheet to the LEFT. */
@keyframes walkAnimation {
  from {
    transform: translate3d(0%,0%,0);
  }
  to {
    transform: translate3d(-100%,0%,0);
  }
}

/* 
	One spritesheet containing layout of 4 by 4 frames, each designed at 32x32.
	Total width = 128px multiplied by dynamic pixel size.
	The visible part is cropped out via .Character's overflow:hidden rule. This image lives *inside* the .Character container.
*/
.Character_sprite-sheet {
	width: calc(var(--pixel-size) * 128px);
	animation: walkAnimation 0.6s steps(4) infinite; 
   /* ^ "Complete the animation in 4 distinct steps". We use 4 because each row of the spritesheet has 4 frames. */
	position: absolute;
	top:0;
	left:0;
   
	display: none; /* For the sake of this demo, JS will control which character spritesheet is visible */
}
.Character_sprite-sheet.active {
	display: block;
}

/* 
	Set up classes that change the y position of underlying spritesheet.
	Spritesheet is designed with this layout:
		D1 D2 D3 D4
		R1 R2 R3 R4 (32px away from top)
		U1 U2 U3 U4 (64px away from top)
		L1 L2 L3 L4 (96px away from top)

   EX: https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-HANK-2-SHEET.png
*/

.Character--walk-down .Character_sprite-sheet {
	top: 0;
}
.Character--walk-right .Character_sprite-sheet {
	top: calc(var(--pixel-size) * -32px);
}
.Character--walk-up .Character_sprite-sheet {
	top: calc(var(--pixel-size) * -64px);
}
.Character--walk-left .Character_sprite-sheet {
	top: calc(var(--pixel-size) * -96px);
}


/* Demo UI Stuff ------------------------------------------------- */
* {
	box-sizing: border-box;
}
body {
	margin: 0;
	padding: 0;
	background-image: linear-gradient(180deg, #5DCBE4 0%, #47BCE4 100%);
	height: 100%;
   min-height: 100vh;
	padding-top: calc(var(--pixel-size) * 18px);
	font-family: 'Dosis', sans-serif;
}
.Container {
	max-width: calc(var(--pixel-size) * 100px);
	margin: 0 auto;
}
.TitleImage {
	width: calc(var(--pixel-size) * 95px);
	display: block;
	margin: 0 auto;
}
.NavigationBubble {
	-webkit-appearance: none;
	width: calc(var(--pixel-size) * 3px);
	height: calc(var(--pixel-size) * 3px);
	background: #2497cc;
	border: 0;
	outline: 0;
	cursor: pointer;
	padding:0;
	margin-left: 0.5em;
	margin-right: 0.5em;
	box-sizing:content-box;
}
.NavigationBubble:hover:not(.active) {
	opacity: 0.9;
}
.NavigationBubble.active {
	background: #fff;
}
.NextSpritesheetButton {
	-webkit-appearance: none;
	border:0;
	background:none;
	padding: 1em;
	position: absolute;
	top: 50%;
}
.NextSpritesheetButton--prev {
	left: 0;
}
.NextSpritesheetButton--next {
	right: 0;
}
.NextSpritesheetButton:hover {
	cursor: pointer;
	background: rgba(255,255,255,0.2);
}
.NextSpritesheetButton:focus {
	outline:none;
}
.NextSpritesheetButton svg {
	display: block;
	width: calc(var(--pixel-size) * 4px);
}
.DirectionArrow {
	background: none;
	outline: 0;
	border: 0;
	cursor: pointer;
}
.DirectionArrow svg {
	display: block;
	width: calc(var(--pixel-size) * 13px);
}
.DirectionArrow--active .Arrow_outline-top { stroke: #5268ec; }
.DirectionArrow--active .Arrow_surface { stroke: #639bff; }
.DirectionArrow--active .Arrow_outline-bottom { stroke: #164496; }
.DirectionArrow--active .Arrow_edge { stroke: #7baaff; }
.DirectionArrow--active .Arrow_front { stroke: #4c8bfd }
.DirectionArrow--active .Arrow_arrow-inset { stroke: #fff; }
.DirectionArrow--active .Arrow_arrow-body { stroke: #fff; }
.SpritesheetSlider {
	position: relative;
	margin-bottom: calc(var(--pixel-size) * 10px);	
}
.flex-center {
	display:flex;
	justify-content: center;
}

footer {
   margin-top: calc(var(--pixel-size) * 20px);
}
@media (min-height: 515px) {
   footer {
      position: absolute;
      left:0;
      right:0;
      bottom:0;
   }
}
footer p {
	color: #fff;
	text-align: center;
	padding: 0.8em;
	font-size: 18px;
   margin: 0;
   border-top: 1px solid rgba(255,255,255,0.4)
}
footer a {
	font-weight:bold;
	font-size: 18px;
	color: #164496;
}

//This stuff just makes the demo's UI work. ---------------
var characterElement = document.querySelector(".Character");

var spritesheets = [
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-HANK-2-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-EMMY-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-SHIRMOND-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-SARA-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-PATTY-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-JESSIE-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-KIM-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-MINDY-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-ZAK-SHEET.png",
   "https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-BEAR-SHEET.png",
];

let activeIndex = 0;
let spritesheetElements = "";
let navigationElements = "";


spritesheets.forEach((spritesheet, index) => {
	spritesheetElements += `<img src="${spritesheet}" class="PixelArtImage Character_sprite-sheet index-${index}" />`
	navigationElements += `<button class="NavigationBubble index-${index}" onclick='setActive(${index})' />`
});
characterElement.insertAdjacentHTML( 'beforeend', spritesheetElements );

document.querySelector(".Navigation").insertAdjacentHTML( 'beforeend', navigationElements );

function setActive(index) {
	activeIndex = index;
	document.querySelectorAll(`.active`).forEach(node => {
		node.classList.remove("active")
	})
	document.querySelectorAll(`.index-${index}`).forEach(node => {
		node.classList.add("active")
	})
}

function setDirection(direction) {
	[
		"Character--walk-down",
		"Character--walk-right",
		"Character--walk-up",
		"Character--walk-left"
	].forEach(className => {
		characterElement.classList.remove(className)
	})


	document.querySelector(".DirectionArrow--active").classList.remove("DirectionArrow--active")

	var directionClass = "Character--walk-down";
	if (direction === "DOWN") {
		document.querySelector(".DirectionArrow-down").classList.add("DirectionArrow--active")
	}

	if (direction === "LEFT") { 
		directionClass = "Character--walk-left" 
		document.querySelector(".DirectionArrow-left").classList.add("DirectionArrow--active")
	}
	if (direction === "RIGHT") { 
		directionClass = "Character--walk-right" 
		document.querySelector(".DirectionArrow-right").classList.add("DirectionArrow--active")
	}
	if (direction === "UP") { 
		directionClass = "Character--walk-up" 
		document.querySelector(".DirectionArrow-up").classList.add("DirectionArrow--active")
	}

	characterElement.classList.add(directionClass)
}

function setPreviousActive() {
	activeIndex = activeIndex > 0 ? activeIndex - 1 : spritesheets.length - 1;
	setActive(activeIndex)
}

function setNextActive() {
	activeIndex = activeIndex < spritesheets.length - 1 ? activeIndex + 1 : 0;
	setActive(activeIndex)	
}

//Kick it off!
setActive(activeIndex);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.