Talking face

by Raul Bojalil

HTML

<div style="position: relative">
  <div id="face" style="position: absolute" class="face"></div>
  <div id="blink" style="position: absolute; display: none" class="face face-eyes-closed"></div>
</div>

CSS

img,div { 
        image-rendering: optimizeSpeed;             /* STOP SMOOTHING, GIVE ME SPEED  */
        image-rendering: -moz-crisp-edges;          /* Firefox                        */
        image-rendering: -o-crisp-edges;            /* Opera                          */
        image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */
        image-rendering: pixelated;                 /* Universal support since 2021   */
        image-rendering: optimize-contrast;         /* CSS3 Proposed                  */
        -ms-interpolation-mode: nearest-neighbor;   /* IE8+                           */
    }

.face {
  width: 32px;
  transform: scale(15);
  height: 32px;
  background: url('https://static.wixstatic.com/media/7032bc_bb6c9eaff11243a4aceb2ade7cbeef1a~mv2.png/v1/fill/w_416,h_32,al_c,lg_1,q_85,enc_auto/7032bc_bb6c9eaff11243a4aceb2ade7cbeef1a~mv2.png'); 
}

body {
  display:flex;
  flex-direction:column;
  justify-content:center;
  min-height:100vh;
  align-content: center;
  align-items: center;
  height:100%;
}

.face-a {
  background-position-x: 0px;
}

.face-l {
  background-position-x: -32px;
}

.face-ee {
  background-position-x: -64px;
}

.face-b {
  background-position-x: -96px;
}

.face-f {
  background-position-x: -128px;
}

.face-o {
  background-position-x: -160px;
}

.face-u {
  background-position-x: -192px;
}

.face-r {
  background-position-x: -224px;
}

.face-c {
  background-position-x: -256px;
}

.face-eyes-closed {
  background-position-x: -384px;
}

JavaScript

const face = document.getElementById("face");
const blink = document.getElementById("blink");

const phonemes = {
	a: "face-a",
  b: "face-b",
  c: "face-c",
  d: "face-d",
  e: "face-a",
  f: "face-f",
  g: "face-c",
  h: "face-c",
  i: "face-ee",
  j: "face-c",
  k: "face-c",
  l: "face-l",
  m: "face-b",
  n: "face-c",
  o: "face-o",
  p: "face-b",
  q: "face-c",
  r: "face-r",
  s: "face-c",
  t: "face-c",
  u: "face-u",
  v: "face-b",
  w: "face-u",
  x: "face-c",
  y: "face-c",
  z: "face-c",
};

const phrase = "el akbal es tonto";

const processedPhrase = phrase.replaceAll(" ", "").toLowerCase();

const removeExpression = () => {
	
  for(const phoneme in phonemes) {
  	face.classList.remove(phonemes[phoneme]);
  }
}

var index = -1;
var time = 0;
var blinkTime = 0;
var startTime = window.mozAnimationStartTime || Date.now();
function animate(timestamp) {
  const drawStart = (timestamp || Date.now());
  var diff = drawStart - startTime;

  if (diff <= 0)
  {
    diff = 16;
  }

  startTime = drawStart;
  requestAnimationFrame(animate);
  
 	blinkTime += diff;
  

  
  if (blinkTime > 2000) {
  
  	blink.style.display = "";
  	if (blinkTime > 2100) {
    	blink.style.display = "none";
      blinkTime = 0;
    }
  }
 
  if (index < processedPhrase.length) {
  	time += diff;
  	if (time > 100) {
  		index ++;
  		time = 0;
    
    	if (index < processedPhrase.length) {
      	const current = processedPhrase[index];

        const phonemeClass = phonemes[current];

        removeExpression();
        face.classList.add(phonemeClass);
      } else {
      	removeExpression();
        face.classList.add(phonemes["m"])
      }

    }
  
  }
}
animate();