Présentation

Devoir mettre à jour son Sub Goal est une tâche répétitive qui fait perdre du temps et n’apporte aucune valeur ajoutée à votre live si vous le modifiez en direct.

Ce Guide va vous expliquer comment créer un widget dans StreamElements vous permettant d’avoir un Sub / Bits / Follow Goal qui se met à jour automatiquement.

J’entends par là que si vous mettez une incrémentation de 10 subs à chaque fois, lorsque vous atteindrez 30 subs, le widget affichera 30/30 puis passera à 30/40, et lorsque vous atteindrez 40 subs, 40/40 passera à 40/50 etc…

C’est vous qui décidez de l’incrémentation, si vous voulez un palier tous les 100 subs c’est possible, faisant ainsi passer automatiquement votre objectif de 500/500 à 500/600 par exemple.

J’ai également mis en place un Sub Goal avec jusqu’à 10 paliers personnalisés. Plutôt que d’incrémenter le palier à chaque fois et de n’afficher que l’objectif de subs, il affiche ce que vous proposez aux spectateurs en retour lorsque vous atteignez votre objectif.

Le fait d’avoir 10 paliers personnalisés permet de prévoir à l’avance les différents objectifs et de passer de l’un à l’autre automatiquement.

Vous pouvez par exemple dire qu’à 100 subs vous faites un live karaoké et à 200 subs un live cuisine. Le widget affichera donc 75/100 (Live Karaoké) et dès que vous dépassez l’objectif, 108/200 (Live Cuisine)

Mettre en place le widget

Pour commencer, connectez-vous sur StreamElements, rendez-vous dans Mes Overlays et cliquez sur Nouvelle Overlay.

Laissez la résolution 1080p et cliquez sur Start.

Pensez à renommer votre Overlay puis cliquez sur Add Widget.

Sélectionnez Static/Custom puis Custom widget.

Ouvrez l’éditeur en cliquant sur Open Editor.

Une fois dans l’éditeur, remplacez le code des onglets HTML, CSS, JS et FIELDS par le code du widget que vous voulez implémenter. Vous trouverez le code des widgets plus bas dans ce guide.

Puis cliquez sur Done.

La largeur de la barre originale est de 750, ce qui signifie qu’elle est plus grande que la largeur d’affichage par défaut du custom widget.

Vous pouvez soit réduire la largeur de la barre en changeant sa valeur ou étirer la largeur de l’affichage en passant votre souris sur le bord droit et en l’étirant vers la droite.

Vous pouvez customiser différents aspects du widget grâce aux paramètres dans la colonne de gauche.

Incrémentation : augmente votre objectif de X à chaque fois qu’il est atteint. Exemple : 10 d’incrémentation, lorsque vous atteignez 20/20 vous passez à 20/30, puis 30/40, etc…

Largeur de la barre : plutôt explicite…

Largeur minimum de la barre en % : Si vous voulez que la barre soit toujours un minimum rempli pour afficher le texte vous pouvez jouer sur cette valeur.

Texte dans la barre de chargement : le texte qui apparait avant votre nombre de Subs / Bits / Followers actuels.

Couleur(s) : vous permet de modifier les couleurs des différentes parties du widget.

Pensez à sauvegarder en cliquant sur Save puis à ajouter votre widget dans OBS en copiant le lien de l’overlay.

Le Mini Sub Goal

<link href="https://fonts.googleapis.com/css?family=Karla:400,700" rel="stylesheet">
<style class="text/css">
#progress #bar {
  width: {{bar_width}}px;
  background-color: {{fill_color_bg}};
}
#progress label{
  max-width: {{bar_width}}px;
}
#progress #bar .loading {
  background-color: {{fill_color}};
  min-width: {{min_width}}%;
  color: {{fill_color_label}};
}
#progress #bar .loading .amount {
  color: {{fill_color_current}};
}
#progress #bar .endgame .amount {
  color: {{fill_color_goal}};
}
</style>
<div id="overlay">
  <div id="progress">
    <div id="bar">
      <span class="endgame"><span class="amount">0</span></span>
      <div class="loading"><span>{{main_label}}&nbsp;</span><span class="amount">0</span></div>
    </div>
  </div>
</div>
#progress {
  float: left;
  font-family: Karla, sans-serif;
  font-size: 23px;
  line-height: 23px;
  color: white;
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  width: 800px;
  align-items: left;
  justify-content: flex-end; }
#progress label {
   align-self: flex-start;
   height: 20px;
   padding: .7em 1em;
   border-radius: 10px;
   background: rgba(23, 23, 23, 0.6);
   display: flex;
   flex-basis: fit-content;
   max-width: 400px;
   align-items: center; }
#progress #bar {
   width: 400px;
   max-width: 750px;
   height: 20px;
   padding: .7em 1em;
   border-radius: 10px;
   background: rgba(23, 23, 23, 0.4);
   display: flex;
   align-items: center;
   justify-content: flex-end;
   position: relative;
   flex-wrap: wrap; }
#progress #bar::before, #progress #bar .loading {
   content: '';
   background: rgba(23, 23, 23, 0.6);
   border-radius: 10px;
   width: 100%;
   height: 100%;
   position: absolute;
   left: 0;
   top: 0;
   z-index: -2; }
#progress #bar .loading {
   background: rgba(13, 166, 255, 0.9);
   min-width: 8%;
   width: 13%;
   z-index: 0;
   display: flex;
   align-items: center;
   justify-content: flex-end;
   transition: .5s all ease-in-out; }
#progress #bar .loading .amount {
   position: relative;
   padding-right: 1em; }
#progress #bar .amount {
   font-weight: bold; }
//Code based on @annedorko's work
// MINI SUB GOAL BAR
var fields;
var totalSubs = 0; // The total number of subs
var dynamicGoal = 0; // The goal changing everytime it is reached
var steps = 5; // The steps between each levels
var percent = doPercent( totalSubs, dynamicGoal, steps ); // Percentage of subs out of a current goal segment
var data;
//** LOAD IN INITIAL WIDGET DATA
//*
//*
window.addEventListener('onWidgetLoad', function (obj) {
  // Get base data
  data = obj["detail"]["session"]["data"];
  const fieldData = obj["detail"]["fieldData"];
  fields = fieldData;
  // Set initial goal data
  steps = fieldData["num_steps"];
  totalSubs = data["subscriber-total"]["count"];
  if ( steps <= 0){
     steps = 1;
  }
  dynamicGoal = setInitialGoal( totalSubs, steps);
  // Set goal live
  reloadGoal();
});

//** UPDATE INFO WIDGET INFORMATION
//
//
window.addEventListener('onEventReceived', function (obj) {
  const listener = obj.detail.listener;
  const event = obj["detail"]["event"];
  if ( listener == 'subscriber-latest' ) {
    totalSubs++;
    // Sub bar
    reloadGoal();
    //wait so if we hit 100% it's shown for 5 sec before changing the dynamicSubGoal
    setTimeout(function(){
		dynamicGoal = setInitialGoal( totalSubs, steps);
      	reloadGoal();
  }, 5000);
  }
});


//** CALCULATION FUNCTIONS FOR MINI SUB GOAL BAR
//
//
function reloadGoal() {
  // Set levels
  // Get goal segment amount*/
  $('#progress .endgame .amount').text( dynamicGoal );
  // Set percent
  percent = doPercent( totalSubs, dynamicGoal, steps );
  // Update goal bar
  $('#progress .loading .amount').text( totalSubs );
  $('#progress .loading').css(
    {
      'width': percent + '%'
    });
}

function setInitialGoal(totalSubs, steps) {
  var modulo = totalSubs % steps;
  var dynamicGoal = totalSubs + (steps - modulo);
	return dynamicGoal;
}

function doPercent( totalSubs, dynamicGoal, steps ) {
  var perc = (totalSubs % steps) / steps;
  if (totalSubs >= dynamicGoal ){
    var perc = 100;
  }
  var amount = perc * 100;
  if ( amount < 0 ) {
    amount = 0;
  }
  if ( amount > 100 ) {
    amount = 100;
  }
  return amount;
}
{
  "num_steps": {
    "type": "number",
    "label": "Incrémentation",
    "value": 5,
    "min": 1
  },
  "bar_width": {
    "type": "number",
    "label": "Largeur de la barre",
    "value": 750
  },
  "min_width": {
    "type": "slider",
    "label": "Largeur minimum de la barre en %",
    "value": 5,
    "min": 0,
    "max": 100,
    "steps": 1
  },
  "main_label": {
    "type": "text",
    "label": "Texte dans la barre de chargement",
    "value": "mini Sub Goal"
  },
  "fill_color_label": {
    "type": "colorpicker",
    "label": "Couleur du texte",
    "value": "white"
  },
  "fill_color": {
    "type": "colorpicker",
    "label": "Couleur de la barre de chargement",
    "value": "blue"
  },
  "fill_color_bg": {
    "type": "colorpicker",
    "label": "Couleur du fond",
    "value": "grey"
  },
  "fill_color_goal": {
    "type": "colorpicker",
    "label": "Couleur Sub Goal",
    "value": "white"
  },
  "fill_color_current": {
    "type": "colorpicker",
    "label": "Couleur Sub Actuel",
    "value": "white"
  }
}

Le Grand Sub Goal

<link href="https://fonts.googleapis.com/css?family=Karla:400,700" rel="stylesheet">
<style class="text/css">

#progress #bar {
  width: {{bar_width}}px;
  background-color: {{fill_color_bg}};
}
#progress{
  width: {{bar_width}}px;
}
#progress label {
  background: {{fill_color_bg_text_goal}};
  color: {{fill_color_text_goal}};
}
  
#progress #bar .loading {
  background-color: {{fill_color}};
  min-width: {{min_width}}%;
  color: {{fill_color_label}};
}
#progress #bar .loading .amount {
  color: {{fill_color_current}};
}
#progress #bar .endgame .amount {
  color: {{fill_color_goal}};
}
  

</style>
<div id="overlay">
  <div id="progress">
    <div id="bar">
      <span class="endgame"><span class="amount">0</span></span>
      <div class="loading"><span>{{main_label}}&nbsp;</span><span class="amount">0</span></div>
    </div>
    <label>
      &nbsp;<span id="current_goal">Stream Title</span>
	</label>
  </div>
</div>
#progress {
  float: left;
  font-family: Karla, sans-serif;
  font-size: 23px;
  line-height: 23px;
  color: white;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  width: 750px;
  max-width: 750px;
  align-items: left;}
  #progress label {
    align-self: flex-end;
    height: 5px;
    padding: .7em 1em;
    border-radius: 10px;
    background: rgba(23, 23, 23, 0.6);
    display: flex;
	flex-grow: fit-content;
   	max-width: 750px;
    align-items: center;
    justify-content: flex-end;}
  #progress #bar {
    align-self: flex-start;
    width: 400px;
    max-width: 750px;
    height: 20px;
    padding: .7em 1em;
    border-radius: 10px;
    background: rgba(23, 23, 23, 0.4);
    display: flex;
    align-items: center;
    justify-content: flex-end;
    position: relative;
    flex-wrap: wrap; }
   #progress #bar::before, #progress #bar .loading {
      content: '';
      background: rgba(23, 23, 23, 0.6);
      border-radius: 10px;
      width: 100%;
      height: 100%;
      position: absolute;
      left: 0;
      top: 0;
      z-index: -2; }
    #progress #bar .loading {
      background: rgba(13, 166, 255, 0.9);
      min-width: 8%;
      width: 13%;
      z-index: 0;
      display: flex;
      align-items: center;
      justify-content: flex-end;
      transition: .5s all ease-in-out; }
    #progress #bar .loading .amount {
        position: relative;
        padding-right: 1em; }
	#progress #bar .amount {
      	font-weight: bold; }
//Code based on @annedorko's work
//SUB GOAL BAR
var maxLevel = 5; // Maximum levels to break through
var fields;
var totalSubs = 0; // The total number of subs
var percent; // Percentage of subs out of a current goal
var goalList;
var level = 1;
var data;
//** LOAD IN INITIAL WIDGET DATA
//*
//*
window.addEventListener('onWidgetLoad', function (obj) {
  // Get base data
  data = obj["detail"]["session"]["data"];
  const fieldData = obj["detail"]["fieldData"];
  fields = fieldData;
  // Set initial goal data
  maxLevel = fieldData["num_goals"];
  steps = fieldData["num_steps"];
  totalSubs = data["subscriber-total"]["count"];
  // Set the 10 goals list
  goalList = [
    [fieldData["level_1_goal"],fieldData["level_1"]],
    [fieldData["level_2_goal"],fieldData["level_2"]],
    [fieldData["level_3_goal"],fieldData["level_3"]],
    [fieldData["level_4_goal"],fieldData["level_4"]],
    [fieldData["level_5_goal"],fieldData["level_5"]],
    [fieldData["level_6_goal"],fieldData["level_6"]],
    [fieldData["level_7_goal"],fieldData["level_7"]],
    [fieldData["level_8_goal"],fieldData["level_8"]],
    [fieldData["level_9_goal"],fieldData["level_9"]],
    [fieldData["level_10_goal"],fieldData["level_10"]],
  ];
  level = getLevel( totalSubs, goalList );
  goal = levelGoal( level, fields );
  label = levelLabel( level, fields );
  // Set goal live
  reloadGoal();
});

//** UPDATE INFO WIDGET INFORMATION
//
//
window.addEventListener('onEventReceived', function (obj) {
  const listener = obj.detail.listener;
  const event = obj["detail"]["event"];
  if ( listener == 'subscriber-latest' ) {
    totalSubs++;
    // Sub bar
    reloadGoal();
    //wait so if we hit 100% it's shown for 5 sec before changing the dynamicSubGoal
    setTimeout(function(){
      	goal = levelGoal( level, fields );
      	label = levelLabel( level, fields );
      	reloadGoal();
  }, 5000);
  }
});


//** CALCULATION FUNCTIONS FOR SUB GOAL BAR
//
//
function reloadGoal() {
  // Set levels
  level = getLevel( totalSubs, goalList );
  // Get goal segment amount*/
  $('#progress .endgame .amount').text( goal );
  // Set percent
  percent = doPercent( totalSubs, goal);
  // Update goal bar
  $('#progress .loading .amount').text( totalSubs );
  $('#progress .loading').css(
    {
      'width': percent + '%'
    });
  $('#progress #current_goal').text( label );
}

function getLevel( totalSubs, goalList ) {
  while (level < maxLevel && totalSubs >= goalList[level-1][0]){
    level = level+1;
  }
  return level;
}

function levelLabel( level, fields ) {
  var labelName = 'level_' + level;
  var label = fields[labelName];
  return label;
}

function levelGoal( level, fields ) {
  var goalName = 'level_' + level + '_goal';
  var goal = fields[goalName];
  return goal;
}

function doPercent( totalSubs, goal ) {
  var perc = totalSubs / goal;
  if (totalSubs >= goal){
    var perc = 100;
  }
  var amount = perc * 100;
  if ( amount < 0 ) {
    amount = 0;
  }
  if ( amount > 100 ) {
    amount = 100;
  }
  return amount;
}
{
  "bar_width": {
    "type": "number",
    "label": "Largeur de la barre",
    "value": 800
  },
  "min_width": {
    "type": "slider",
    "label": "Largeur minimum de la barre",
    "value": 5,
    "min": 0,
    "max": 100,
    "steps": 1
  },
  "main_label": {
    "type": "text",
    "label": "Texte dans la barre de chargement",
    "value": "Sub Goal"
  },
  "fill_color_label": {
    "type": "colorpicker",
    "label": "Couleur du texte",
    "value": "white"
  },
  "fill_color": {
    "type": "colorpicker",
    "label": "Couleur de la barre de chargement",
    "value": "blue"
  },
  "fill_color_bg": {
    "type": "colorpicker",
    "label": "Couleur du fond",
    "value": "grey"
  },
  "fill_color_goal": {
    "type": "colorpicker",
    "label": "Couleur Sub Goal",
    "value": "white"
  },
  "fill_color_current": {
    "type": "colorpicker",
    "label": "Couleur Sub Actuel",
    "value": "white"
  },
  "fill_color_text_goal": {
    "type": "colorpicker",
    "label": "Couleur des titres des Goals",
    "value": "white"
  },
  "fill_color_bg_text_goal": {
    "type": "colorpicker",
    "label": "Couleur de fond des titres",
    "value": "rgba(23, 23, 23, 0.6)"
  },
  "num_goals": {
    "type": "slider",
    "label": "Nombre de Goals (Max 10)",
    "value": 5,
    "min": 1,
    "max": 10,
    "steps": 1
  },
  "level_1": {
    "type": "text",
    "label": "Titre Sub Goal 1",
    "value": ""
  },
  "level_1_goal": {
    "type": "number",
    "label": "Sub Goal 1",
    "value": 10,
    "steps": 1
  },
  "level_2": {
    "type": "text",
    "label": "Titre Sub Goal 2",
    "value": ""
  },
  "level_2_goal": {
    "type": "number",
    "label": "Sub Goal 2",
    "value": 20,
    "steps": 1
  },
  "level_3": {
    "type": "text",
    "label": "Titre Sub Goal 3",
    "value": ""
  },
  "level_3_goal": {
    "type": "number",
    "label": "Sub Goal 3",
    "value": 30,
    "steps": 1
  },
  "level_4": {
    "type": "text",
    "label": "Titre Sub Goal 4",
    "value": ""
  },
  "level_4_goal": {
    "type": "number",
    "label": "Sub Goal 4",
    "value": 40,
    "steps": 1
  },
  "level_5": {
    "type": "text",
    "label": "Titre Sub Goal 5",
    "value": ""
  },
  "level_5_goal": {
    "type": "number",
    "label": "Sub Goal 5",
    "value": 50,
    "steps": 1
  },
  "level_6": {
    "type": "text",
    "label": "Titre Sub Goal 6",
    "value": ""
  },
  "level_6_goal": {
    "type": "number",
    "label": "Sub Goal 6",
    "value": 60,
    "steps": 1
  },
  "level_7": {
    "type": "text",
    "label": "Titre Sub Goal 7",
    "value": ""
  },
  "level_7_goal": {
    "type": "number",
    "label": "Sub Goal 7",
    "value": 70,
    "steps": 1
  },
  "level_8": {
    "type": "text",
    "label": "Titre Sub Goal 8",
    "value": ""
  },
  "level_8_goal": {
    "type": "number",
    "label": "Sub Goal 8",
    "value": 80,
    "steps": 1
  },
  "level_9": {
    "type": "text",
    "label": "Titre Sub Goal 9",
    "value": ""
  },
  "level_9_goal": {
    "type": "number",
    "label": "Sub Goal 9",
    "value": 90,
    "steps": 1
  },
  "level_10": {
    "type": "text",
    "label": "Titre Sub Goal 10",
    "value": ""
  },
  "level_10_goal": {
    "type": "number",
    "label": "Sub Goal 10",
    "value": 100,
    "steps": 1
  }
}

Le Bits Goal

<link href="https://fonts.googleapis.com/css?family=Karla:400,700" rel="stylesheet">
<style class="text/css">
#progress #bar {
  width: {{bar_width}}px;
  background-color: {{fill_color_bg}};
}
#progress label{
  max-width: {{bar_width}}px;
}
#progress #bar .loading {
  background-color: {{fill_color}};
  min-width: {{min_width}}%;
}
</style>
<div id="overlay">
  <div id="progress">
    <div id="bar">
      <span class="endgame"><span class="amount"></span></span>
      <div class="loading"><span>{{main_label}}&nbsp;</span><span class="amount"><span><span></span></div>
    </div>
  </div>
</div>
#progress {
  float: left;
  font-family: Karla, sans-serif;
  font-size: 23px;
  line-height: 23px;
  color: white;
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  width: 800px;
  align-items: left;
  justify-content: flex-end; }
  #progress label {
    align-self: flex-start;
    height: 20px;
    padding: .7em 1em;
    border-radius: 10px;
    background: rgba(23, 23, 23, 0.6);
    display: flex;
    flex-basis: fit-content;
   	max-width: 400px;
    align-items: center; }
  #progress #bar {
    width: 400px;
    max-width: 750px;
    height: 20px;
    padding: .7em 1em;
    border-radius: 10px;
    background: rgba(23, 23, 23, 0.4);
    display: flex;
    align-items: center;
    justify-content: flex-end;
    position: relative;
    flex-wrap: wrap; }
   #progress #bar::before, #progress #bar .loading {
      content: '';
      background: rgba(23, 23, 23, 0.6);
      border-radius: 10px;
      width: 100%;
      height: 100%;
      position: absolute;
      left: 0;
      top: 0;
      z-index: -2; }
    #progress #bar .loading {
      background: rgba(13, 166, 255, 0.9);
      min-width: 8%;
      width: 13%;
      z-index: 0;
      display: flex;
      align-items: center;
      justify-content: flex-end;
      transition: .5s all ease-in-out; }
    #progress #bar .loading .amount {
        position: relative;
        padding-right: 1em; }
	#progress #bar .amount {
      	font-weight: bold; }
//Code based on @annedorko's work
// BITS GOAL BAR
var fields;
var totalCheers = 0; // The total number of cheers/bits
var dynamicGoal = 0; // The goal changing everytime it is reached
var steps; // The steps between each levels
var percent; // Percentage of cheers/bits out of a current goal segment
var data;
//** LOAD IN INITIAL WIDGET DATA
//*
//*
window.addEventListener('onWidgetLoad', function (obj) {
  // Get base data
  data = obj["detail"]["session"]["data"];
  const fieldData = obj["detail"]["fieldData"];
  fields = fieldData;
  // Set initial goal data
  steps = fieldData["num_steps"];
  totalCheers = data["cheer-total"]["amount"];
  if ( steps <= 0){
     steps = 1;
  }
  dynamicGoal = setInitialGoal( totalCheers, steps);
  // Set goal live
  reloadGoal();
});

//** UPDATE INFO WIDGET INFORMATION
//
//
window.addEventListener('onEventReceived', function (obj) {
  const listener = obj.detail.listener;
  const event = obj["detail"]["event"];
  if ( listener == 'cheer-latest' ) {
    totalCheers = totalCheers + data["cheer-latest"]["amount"];
    // cheer/bits bar
    reloadGoal();
    //wait so if we hit 100% it's shown for 5 sec before changing the dynamicSubGoal
    setTimeout(function(){
		dynamicGoal = setInitialGoal( totalCheers, steps);
      	reloadGoal();
  }, 5000);
  }
});


//** CALCULATION FUNCTIONS FOR BITS BAR
//
//
function reloadGoal() {
  // Set percent
  percent = doPercent( totalCheers, dynamicGoal, steps );
  // Update goal bar
  $('#progress .loading .amount').text( percent + "%" );
  $('#progress .loading').css(
    {
      'width': percent + '%'
    });
}

function setInitialGoal(totalCheers, steps) {
  var modulo = totalCheers % steps;
  var dynamicGoal = totalCheers + (steps - modulo);
	return dynamicGoal;
}

function doPercent( totalCheers, dynamicGoal, steps ) {
  var perc = (totalCheers % steps) / steps;
  if (totalCheers >= dynamicGoal ){
    var perc = 100;
  }
  var amount = perc * 100;
  if ( amount < 0 ) {
    amount = 0;
  }
  if ( amount > 100 ) {
    amount = 100;
  }
  return amount.toFixed(2);
}
{
  "num_steps": {
    "type": "number",
    "label": "Incrémentation",
    "value": 5,
    "min": 1
  },
  "bar_width": {
    "type": "number",
    "label": "Largeur de la barre",
    "value": 800
  },
  "min_width": {
    "type": "slider",
    "label": "Largeur minimum de la barre",
    "value": 5,
    "min": 0,
    "max": 100,
    "steps": 1
  },
  "main_label": {
    "type": "text",
    "label": "Texte dans la barre de chargement",
    "value": "Bits Goal"
  },
  "fill_color": {
    "type": "colorpicker",
    "label": "Couleur de la barre de chargement",
    "value": "blue"
  },
  "fill_color_bg": {
    "type": "colorpicker",
    "label": "Couleur du fond",
    "value": "grey"
  }
}

Le Followers Goal

<link href="https://fonts.googleapis.com/css?family=Karla:400,700" rel="stylesheet">
<style class="text/css">
#progress #bar {
  width: {{bar_width}}px;
  background-color: {{fill_color_bg}};
}
#progress label{
  max-width: {{bar_width}}px;
}
#progress #bar .loading {
  background-color: {{fill_color}};
  min-width: {{min_width}}%;
}
</style>
<div id="overlay">
  <div id="progress">
    <div id="bar">
      <span class="endgame"><span class="amount">0</span></span>
      <div class="loading"><span>{{main_label}}&nbsp;</span><span class="amount">0</span></div>
    </div>
  </div>
</div>
#progress {
  float: left;
  font-family: Karla, sans-serif;
  font-size: 23px;
  line-height: 23px;
  color: white;
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  width: 800px;
  align-items: left;
  justify-content: flex-end; }
  #progress label {
    align-self: flex-start;
    height: 20px;
    padding: .7em 1em;
    border-radius: 10px;
    background: rgba(23, 23, 23, 0.6);
    display: flex;
    flex-basis: fit-content;
   	max-width: 400px;
    align-items: center; }
  #progress #bar {
    width: 400px;
    max-width: 750px;
    height: 20px;
    padding: .7em 1em;
    border-radius: 10px;
    background: rgba(23, 23, 23, 0.4);
    display: flex;
    align-items: center;
    justify-content: flex-end;
    position: relative;
    flex-wrap: wrap; }
   #progress #bar::before, #progress #bar .loading {
      content: '';
      background: rgba(23, 23, 23, 0.6);
      border-radius: 10px;
      width: 100%;
      height: 100%;
      position: absolute;
      left: 0;
      top: 0;
      z-index: -2; }
    #progress #bar .loading {
      background: rgba(13, 166, 255, 0.9);
      min-width: 8%;
      width: 13%;
      z-index: 0;
      display: flex;
      align-items: center;
      justify-content: flex-end;
      transition: .5s all ease-in-out; }
    #progress #bar .loading .amount {
        position: relative;
        padding-right: 1em; }
	#progress #bar .amount {
      	font-weight: bold; }
// FOLLOW BAR
var fields;
var totalFollows = 0; // The total number of follows
var dynamicGoal = 0; // The goal changing everytime it is reached
var steps = 5; // The steps between each levels
var percent = doPercent( totalFollows, dynamicGoal, steps ); // Percentage of donations out of a current goal segment
var data;
//** LOAD IN INITIAL WIDGET DATA
//*
//*
window.addEventListener('onWidgetLoad', function (obj) {
  // Get base data
  data = obj["detail"]["session"]["data"];
  const fieldData = obj["detail"]["fieldData"];
  fields = fieldData;
  // Set initial goal data
  steps = fieldData["num_steps"];
  totalFollows = data["follower-total"]["count"];
  if ( steps <= 0){
     steps = 1;
  }
  dynamicGoal = setInitialGoal( totalFollows, steps);
  // Set goal live
  reloadGoal();
});

//** UPDATE INFO WIDGET INFORMATION
//
//
window.addEventListener('onEventReceived', function (obj) {
  const listener = obj.detail.listener;
  const event = obj["detail"]["event"];
  if ( listener == 'follower-latest' ) {
	//totalFollows = data["subscriber-total"]["count"];
    totalFollows++;
    // Sub bar
    reloadGoal();
    //wait so if we hit 100% it's shown for 5 sec before changing the dynamicSubGoal
    setTimeout(function(){
		dynamicGoal = setInitialGoal( totalFollows, steps);
      	reloadGoal();
  }, 5000);
  }
});


//** CALCULATION FUNCTIONS FOR DONATIONS BAR
//
//
function reloadGoal() {
  // Set levels
  // Get goal segment amount*/
  $('#progress .endgame .amount').text( dynamicGoal );
  // Set percent
  percent = doPercent( totalFollows, dynamicGoal, steps );
  // Update goal bar
  $('#progress .loading .amount').text( totalFollows );
  $('#progress .loading').css(
    {
      'width': percent + '%'
    });
}

function setInitialGoal( totalFollows, steps) {
  var modulo = totalFollows % steps;
  var dynamicGoal = totalFollows + (steps - modulo);
	return dynamicGoal;
}

function doPercent( totalFollows, dynamicGoal, steps ) {
  var perc = (totalFollows % steps) / steps;
  if (totalFollows >= dynamicGoal ){
    var perc = 100;
  }
  var amount = perc * 100;
  if ( amount < 0 ) {
    amount = 0;
  }
  if ( amount > 100 ) {
    amount = 100;
  }
  return amount;
}
{
  "main_label": {
    "type": "text",
    "label": "Texte dans la barre de chargement",
    "value": "!followers"
  },
  "num_steps": {
    "type": "number",
    "label": "Incrémentation",
    "value": 5,
    "min": 1
  },
  "bar_width": {
    "type": "number",
    "label": "Largeur de la barre",
    "value": 800
  },
  "fill_color": {
    "type": "colorpicker",
    "label": "Couleur de la barre de chargement",
    "value": "blue"
  },
  "fill_color_bg": {
    "type": "colorpicker",
    "label": "Couleur du fond",
    "value": "grey"
  },
  "min_width": {
    "type": "slider",
    "label": "Largeur minimum de la barre",
    "value": 5,
    "min": 0,
    "max": 100,
    "steps": 1
  }
}

Le Mini Sub Goal (version texte)

<link href="https://fonts.googleapis.com/css?family={{font_choice}}:400,700" rel="stylesheet">
<style class="text/css">
  
</style>

<div id="overlay">
  <div id="progress">
	
    <span class="loading">{{main_label}}&nbsp;</span>
    <span class="loading"><span class="amount">0</span></span>
    /
    <span class="endgame"><span class="amount">0&nbsp;</span>
    <span class="loading">{{end_label}}&nbsp;</span>
    
  </div>
</div>
#progress {
  float: left;
  font-family: {{font_choice}};
  font-size: {{taille_texte}}px;
  color: {{couleur_texte}};
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: left;
  justify-content: flex-start; }
.loading{
  font-weight: {{weight_choice}};
}
.amount {
  font-weight: {{weight_Sub_choice}};
}
// SUBGOAL BAR
var fields;
var totalSubs = 0; // The total number of subs
var dynamicGoal = 0; // The goal changing everytime it is reached
var steps = 5; // The steps between each levels
var data;
//** LOAD IN INITIAL WIDGET DATA
//*
//*
window.addEventListener('onWidgetLoad', function (obj) {
  // Get base data
  data = obj["detail"]["session"]["data"];
  const fieldData = obj["detail"]["fieldData"];
  fields = fieldData;
  // Set initial goal data
  steps = fieldData["num_steps"];
  totalSubs = data["subscriber-total"]["count"];
  if ( steps <= 0){
     steps = 1;
  }
  dynamicGoal = setInitialGoal( totalSubs, steps);
  // Set goal live
  reloadGoal();
});

//** UPDATE INFO WIDGET INFORMATION
//
//
window.addEventListener('onEventReceived', function (obj) {
  const listener = obj.detail.listener;
  const event = obj["detail"]["event"];
  if ( listener == 'subscriber-latest' ) {
    totalSubs++;
    // Sub bar
    reloadGoal();
    //wait so if we hit 100% it's shown for 5 sec before changing the dynamicSubGoal
    setTimeout(function(){
		dynamicGoal = setInitialGoal( totalSubs, steps);
      	reloadGoal();
  }, 5000);
  }
});


//** CALCULATION FUNCTIONS FOR DONATIONS BAR
//
//
function reloadGoal() {
  // Set levels
  // Get goal segment amount*/
  $('#progress .endgame .amount').text( dynamicGoal );
  // Set percent
  // Update goal bar
  $('#progress .loading .amount').text( totalSubs );
}

function setInitialGoal(totalSubs, steps) {
  var modulo = totalSubs % steps;
  var dynamicGoal = totalSubs + (steps - modulo);
	return dynamicGoal;
}

{
  "main_label": {
    "type": "text",
    "label": "Texte avant le sub goal",
    "value": "!SUB"
  },
  "end_label": {
    "type": "text",
    "label": "Texte après le sub goal",
    "value": "Subgoal"
  },
  "num_steps": {
    "type": "number",
    "label": "Incrémentation",
    "value": 5,
    "min": 1
  },
  "font_choice": {
   	"type": "googleFont",
      "label": "Sélectionner une police",
      "value": "Karla"
  },
  "taille_texte": {
    "type": "slider",
    "label": "Taille du texte",
    "value": 23,
    "min": 1,
    "max": 100,
    "step": 1
  },
  "couleur_texte": {
    "type": "colorpicker",
    "label": "Couleur du texte",
    "value": "#FFFFFF"
  },
  "weight_choice": {
    "type": "dropdown",
    "label": "Options de texte:",
    "value": "normal",
    "options": {
      "normal": "Normal",
      "bold": "Gras"
    }
  },
  "weight_Sub_choice": {
    "type": "dropdown",
    "label": "Options de Sub Goal:",
    "value": "normal",
    "options": {
      "normal": "Normal",
      "bold": "Gras"
    }
  }
}

La vidéo