• code
  • wordpress
  • heroes
  • community
  • developers
  • inspire
Loaded

Create a Talking Avatar with HTML5 and JQuery

In this article we explain how to build an avatar model through static images included in HTML and animate it thanks to JQuery and the browser’s speech synthesis skills.

In general it is a great initial design work and then the pieces are joined in code to be able to shape our avatar. Although this article is written by Cristina, it is a joint work of several WordPress Heroes professionals.

We leave you an example so you can use it as an initial guide. Ideally, you apply the code to your own creations and develop new effects and animations. You can share your experience in the comments.

Create a base design for the avatar

First of all you will have to generate a series of elements that will configure our avatar. In our case we have developed several pieces that we later join in the code. Left arm, right arm, left leg, right leg, trunk, right eye …

In total we have prepared 29 elements that make up our avatar. Keep in mind that even each pupil has its independent element to give more realism to the doll.

If you prepare your own pieces for your avatar you will always have to use a reference on which to place each part of the puzzle. The right arm will take its place in a space. For example if you look at the following image you will see that the eyebrows are placed in a transparency just in the position above the eyes. Superimposing all the images in order we will have our complete avatar.

We leave you a compressed downloadable file so you can use all the images.

Avatar Pieces

All images in png of our avatar in a compressed file

Coming soon
This part may be the most complex if you are not an expert in design. We work with Gimp regularly and the results are very good. However, some experience is necessary, especially to use the transparencies and leave the images adjusted.

Include Avatar images in HTML 5

Once you have all the pieces of the puzzle ready we have to include them on our website through HTML 5. For this we will use the following code that uses WordPress core functions.

Code for physical structure of our Avatar

<div class="wph-avatar-wrapper">
  <div class="wph-avatar position-relative">
    <div class="wph-avatar-image glasses-red display-none">
      <?php echo wp_get_attachment_image(1695, 'full'); ?>
    </div>
    <div class="wph-avatar-image glasses-black display-none">
      <?php echo wp_get_attachment_image(1710, 'full'); ?>
    </div>
    <div class="wph-avatar-image glasses-blue display-none">
      <?php echo wp_get_attachment_image(1711, 'full'); ?>
    </div>
    <div class="wph-avatar-image helmet display-none">
      <?php echo wp_get_attachment_image(1713, 'full'); ?>
    </div>
    <div class="wph-avatar-image arm arm-right">
      <?php echo wp_get_attachment_image(1701, 'full'); ?>
    </div>
    <div class="wph-avatar-image arm arm-right arm-jar-right display-none">
      <?php echo wp_get_attachment_image(1698, 'full'); ?>
    </div>
    <div class="wph-avatar-image arm arm-left">
      <?php echo wp_get_attachment_image(1699, 'full'); ?>
    </div>
    <div class="wph-avatar-image arm arm-left arm-jar-left display-none">
      <?php echo wp_get_attachment_image(1697, 'full'); ?>
    </div>
    <div class="wph-avatar-image legs">
      <?php echo wp_get_attachment_image(1714, 'full'); ?>
    </div>
    <div class="wph-avatar-image chest">
      <?php echo wp_get_attachment_image(1696, 'full'); ?>
    </div>
    <div class="wph-avatar-image face">
      <?php echo wp_get_attachment_image(1709, 'full'); ?>
    </div>
    <div class="wph-avatar-image face eye eyebrows">
      <?php echo wp_get_attachment_image(1702, 'full'); ?>
    </div>
    <div class="wph-avatar-image face eye eye-closed eye-closed-right display-none">
      <?php echo wp_get_attachment_image(1704, 'full'); ?>
    </div>
    <div class="wph-avatar-image face eye eye-closed eye-closed-left display-none">
      <?php echo wp_get_attachment_image(1703, 'full'); ?>
    </div>
    <div class="wph-avatar-image face eye eye-right eye-right">
      <?php echo wp_get_attachment_image(1708, 'full'); ?>
    </div>
    <div class="wph-avatar-image face eye eye-right eye-pupil eye-pupil-right">
      <?php echo wp_get_attachment_image(1707, 'full'); ?>
    </div>
    <div class="wph-avatar-image face eye eye-left eye-left">
      <?php echo wp_get_attachment_image(1705, 'full'); ?>
    </div>
    <div class="wph-avatar-image face eye eye-left eye-pupil eye-pupil-left">
      <?php echo wp_get_attachment_image(1706, 'full'); ?>
    </div>
    <div class="wph-avatar-image face mouth mouth-closed-smile display-none">
      <?php echo wp_get_attachment_image(1715, 'full'); ?>
    </div>
    <div class="wph-avatar-image face mouth mouth-closed display-none">
      <?php echo wp_get_attachment_image(1716, 'full'); ?>
    </div>
    <div class="wph-avatar-image face mouth mouth-smile display-none">
      <?php echo wp_get_attachment_image(1718, 'full'); ?>
    </div>
    <div class="wph-avatar-image face mouth mouth-talking mouth-talking-4 display-none">
      <?php echo wp_get_attachment_image(1694, 'full'); ?>
    </div>
    <div class="wph-avatar-image face mouth mouth-talking mouth-talking-3 display-none">
      <?php echo wp_get_attachment_image(1722, 'full'); ?>
    </div>
    <div class="wph-avatar-image face mouth mouth-talking mouth-talking-2 display-none">
      <?php echo wp_get_attachment_image(1721, 'full'); ?>
    </div>
    <div class="wph-avatar-image face mouth mouth-talking mouth-talking-1">
      <?php echo wp_get_attachment_image(99, 'full'); ?>
    </div>
    <div class="wph-avatar-image face hair">
      <?php echo wp_get_attachment_image(1712, 'full'); ?>
    </div>
  </div>
</div>

And we will also include the following CSS code that regulates the styles of our avatar. It fundamentally uses the position:absolute; property for all images so that they overlap each other.

Code for our Avatar styles
.wph-avatar-image img{
  position: absolute!important;
}

@media all and (max-width:768px){
  .wph-avatar-wrapper{
    width: 80px;
    top: 180px;
  }
}

As you can see simply a global div structure is generated that includes each of the images of our avatar. To load the images we use the WordPress function wp_get_attachment_image with which we call the image that contains a certain ID.

Previously we have uploaded each of the images to our installation through the multimedia file manager of the WordPress administration panel itself.

One of the fundamental details of this code is the choice of the HTML classes of each image. For example, in the elements that correspond to the face we include the face class, the images that are associated with an eye include the eye class …

Then each image has a particular class that differentiates it. There is an image that has the wph-avatar-image, face, eye, eye-left, eye-pupil and eye-pupil-left classes. Each of them will then help us to generate the appropriate JQuery animations for each element.

Of course, in each image the wph-avatar-image class is included, which governs the position styles we discussed before so that the entire avatar is integrated.

An important aspect that we cannot forget is the order in which the images are loaded. HTML includes images as you read them in the code, so the first images in the list will be the ones below the following.

This is key when including each image to know where it will be later in the visualization. If we put the image corresponding to the eye before that of the face it will result in the eye not being seen because the oval corresponding to the image of the face will hide it.

Add animations to an Avatar using Jquery

Once we can have the complete physiognomy of our avatar, we will give it life. For this we will use the JQuery client language, based on JavaScript.

We will simply call each of the classes with which we have named each image and play with them, animating them with different CSS properties to create the magic.

The complete code that we have prepared for the animation you see of the avatar is as follows. Do not panic. This is the most complex code of all since it includes many different types of interactions with CSS and with the elements of the DOM that give life to the avatar. We explain it below in parts.

Code for our Avatar animations

jQuery(document).ready(function($) {
  function wph_avatar_rotate(targetElement, speed, angle_initial, angle_final){
    $({deg: angle_initial}).animate({deg: angle_final}, {
      duration: speed,
      step: function(now) {
        targetElement.find('img').css({
          transform: 'rotate(' + now + 'deg)'
        });
      }
    });
  };

  function wph_avatar_blink(target_eye = 'both', speed = 200){
    setTimeout(function(){
      $('.eye:not(.eye-closed):not(.eyebrows)' + (target_eye != 'both' ? ':not(.eye-' + target_eye + ')' : '')).addClass('display-none');
      $('.eye-closed').removeClass('display-none');
      $('.wph-avatar-image.eyebrows img').animate({top:'+1px'}, 100);
    }, 500);
    setTimeout(function(){
      $('.eye-closed').addClass('display-none');
      $('.eye:not(.eye-closed):not(.eyebrows)' + (target_eye != 'both' ? ':not(.eye-' + target_eye + ')' : '')).removeClass('display-none');
      $('.wph-avatar-image.eyebrows img').animate({top:'-1px'}, 100);
    }, 500 + speed);
  };

  function wph_avatar_arms(target_arm = 'left'){
    var random = Math.floor((Math.random() * $('.arm-' + target_arm + ':not(.arm-ok)').length));
    $('.arm-' + target_arm + ':not(.arm-ok)').addClass('display-none');
    $('.arm-' + target_arm + ':not(.arm-ok)').eq(random).removeClass('display-none');
  };

  function wph_avatar_eyebrows(distance, speed){
    $('.wph-avatar-image.eyebrows img').animate({top:'-' + distance + 'px'}, speed);
    $('.wph-avatar-image.eyebrows img').queue(function() {
      $('.wph-avatar-image.eyebrows img').animate({top:'+' + distance + 'px'}, speed);
      $(this).dequeue();
    });
  };

  function wph_avatar_pupils(speed){
    $('.wph-avatar-image.eye-pupil img').animate({left:'+1px'}, speed);
    $('.wph-avatar-image.eye-pupil img').queue(function() {
      $('.wph-avatar-image.eye-pupil img').animate({left:'-1px'}, speed);
      $(this).dequeue();
    });
  };

  function wph_avatar_mouth(){
    var random = Math.floor((Math.random() * $('.wph-avatar-image.mouth').length));
    $('.wph-avatar-image.mouth').addClass('display-none');
    $('.wph-avatar-image.mouth').eq(random).removeClass('display-none');
  };

  function wph_avatar_talking(){
    var random = Math.floor((Math.random() * $('.wph-avatar-image.mouth-talking').length));
    $('.wph-avatar-image.mouth').addClass('display-none');
    $('.wph-avatar-image.mouth-talking').eq(random).removeClass('display-none');
  };

  function speak(text, callback) {
    var utterance = new SpeechSynthesisUtterance();
    utterance.text = text;
    utterance.lang = 'es-ES';
    utterance.rate = 1;
    var wph_speak_interval = setInterval(function(){
      wph_avatar_talking();
    }, Math.floor((Math.random() * 150) + 150));
  
    utterance.onend = function () {
      wph_avatar_mouth();
      clearInterval(wph_speak_interval);
      if (callback) {
        callback();
      }
    };
 
    utterance.onerror = function (e) {
      if (callback) {
        callback(e);
      }
    };
 
    speechSynthesis.speak(utterance);
  }

  function wph_avatar_idle(){ 
    /*wph_avatar_breathe($('.wph-avatar-image.chest img'), Math.floor((Math.random() * 2000) + 2000));*/

    setInterval(function(){
      setTimeout(function(){wph_avatar_rotate($('.wph-avatar-image.face'), Math.floor((Math.random() * 2000) + 2000), 4, 0);}, 1000);
      setTimeout(function(){wph_avatar_rotate($('.wph-avatar-image.face'), Math.floor((Math.random() * 2000) + 2000), 0, 4);}, 5000);
    }, 10000);

    setInterval(function(){
      setTimeout(function(){wph_avatar_rotate($('.wph-avatar-image.arm-right'), Math.floor((Math.random() * 2000) + 2000), 1, 0);}, 1000);
      setTimeout(function(){wph_avatar_rotate($('.wph-avatar-image.arm-right'), Math.floor((Math.random() * 2000) + 2000), 0, 1);}, 3000);
    }, 9000);

    setInterval(function(){
      setTimeout(function(){wph_avatar_rotate($('.wph-avatar-image.arm-left'), Math.floor((Math.random() * 2000) + 2000), 1, 0);}, 3000);
      setTimeout(function(){wph_avatar_rotate($('.wph-avatar-image.arm-left'), Math.floor((Math.random() * 2000) + 2000), 0, 1);}, 4000); 
    }, 11000);

    setInterval(function(){wph_avatar_blink();}, Math.floor((Math.random() * 2000) + 3000));
    setInterval(function(){wph_avatar_pupils(1000);}, Math.floor((Math.random() * 1000) + 5000));
    setInterval(function(){wph_avatar_mouth();}, Math.floor((Math.random() * 5000) + 7000));
    setInterval(function(){wph_avatar_arms('left');}, Math.floor((Math.random() * 2000) + 3500));
    setInterval(function(){wph_avatar_arms('right');}, Math.floor((Math.random() * 1000) + 3000));
  };

  wph_avatar_idle();
  setTimeout(function(){speak('Marina, muchas felicidades Naná por tus cuatro añitos.', false);}, 3000);
});

In general you will see that there are functions for each part of the body. There is a function that takes care of the mouth, another that takes care of the pupils, another of the movement of the head …

We have used many random elements to give different periods to the movements. You will see that time ranges that govern the amplitude of some movements or their duration are randomly calculated. This makes it more attractive to the user who sees it and is not a simple repetition of patterns.

One of the functions, which makes the avatar speak, we will explain in more detail in another blog article. In the rest of the functions the most interesting is wph_avatar_idle which is the one that connects all and starts the movement process.

As you can see, setInterval and setTimeout are widely used, which are two fundamental JavaScript functions to play with the response times of the DOM elements, both timing them and creating repetitions.

If you want to understand each function in detail you will have to study it in depth and see its interactions with the rest.

Prev
No more posts
Next
Set up and Manage Cron system for WordPress

close
close
close
Stands for NĂşmero de IdentificaciĂłn Fiscal. The spanish unique personal identification number.
Stands for NĂşmero de AfiliaciĂłn. The spanish unique worker identification number.
Stands for CĂłdigo de IdentificaciĂłn Fiscal. The spanish unique identifier for a company.
Stands for CĂłdigo Cuenta de CotizaciĂłn. The number that Spanish Social Security attributes to all entrepreneurs and subjects responsible for paying contributions to the system, to control compliance with contributions and other items.
add
add
add
The system will add this maximum number of minutes to your everyday working times randomly
Max minutes reducing working times
Loading...
close
close