How many year-end review emails did you get this year? Ever since Spotify Wrapped first made waves, it feels like more and more brands have tried to recreate the same hyper-personalized feel, my company included. One of the questions that came up from my team this year:
Can we calculate a percentage comparing someone’s actual progress this year against a goal, or against their actual progress last year?
Absolutely! In this article I’ll give you all the AMPscript you need. And to have even more fun, I’ll show you how to reuse those percentages in dynamically sized HTML progress bars.
Percentages In AMPscript
The first thing we’ll do is select what is being compared — progress vs. a goal, this year vs. last year, personal performance vs. community average, etc. This can be anything you want as long as it’s a number format.
To keep things simple for this example, I’m going to do two sets of progress vs. a goal, and for example purposes I will just hard code some values — one under goal, one over.
%%[
/* GOAL 1 */
set @actual1 = "421"
set @goal1 = "500"
/* GOAL 2 */
set @actual2 = "523"
set @goal2 = "500"
]%%
Second, we’ll use Divide() to get the progress as a decimal.
set @percentage1 = Divide(@actual1,@goal1)
set @percentage2 = Divide(@actual2,@goal2)
Results: 421 / 500 = 0.842, and 523 / 500 = 1.046
Third, we’re going to use Multiply() by 100 to turn our decimals into percentages.
set @percentage1 = Multiply(@percentage1,100)
set @percentage2 = Multiply(@percentage2,100)
(You’ll notice I’m using set on the same variable again — that is supported, if you want to do that to make iterative changes to the same variable. When we get to the end, I’ll give you a single consolidated line with all transformations, but for now let’s keep the steps separate so we can see what each one is doing.)
Results: 0.842 × 100 = 84.2, and 1.046 × 100 = 104.6
We’re getting close, but depending on how large your numbers are, you may end up with a lot of decimal points — so fourth, let’s reformat as a whole number (‘N’) with zero decimal points (‘0’).
set @percentage1 = FormatNumber(@percentage1,'N0')
set @percentage2 = FormatNumber(@percentage2,'N0')
This rounds us to a clean 84 and 105, perfect for showing percentages.
At this point you can use this in your email content; e.g. “You completed 84% of your goal”, but for positive numbers “You completed 105% of your goal” feels a little odd. It feels more natural to use a percentage beat or missed, so let’s calculate that next.
We’ll use Subtract() to take away 100, which will leave us with a positive or negative number less than 100.
set @percentage_beat1 = Subtract(@percentage1,100)
set @percentage_beat2 = Subtract(@percentage2,100)
Results: 84 - 100 = -16, and 105 - 100 = 5
After a quick Replace() to strip off the - (if present)…
set @percentage_beat1 = Replace(@percentage_beat1,'-','')
set @percentage_beat2 = Replace(@percentage_beat2,'-','')
…we’re left with the final numbers, 16 (percent) under, and 5 (percent) over.
Before we get into how to use those final numbers in the email content, here is the complete view of all these steps together.
%%[
/* PERCENTAGE TO GOAL */
/* GOAL 1 */
set @actual1 = "421"
set @goal1 = "500"
set @percentage1 = Divide(@actual1,@goal1) /* Divide the actual vs. the goal */
set @percentage1 = Multiply(@percentage1,100) /* Multiply by 100 for a percent */
set @percentage1 = FormatNumber(@percentage1,'N0') /* Format as a whole number 'N' with 0 decimal points */
set @percentage1 = Replace(@percentage1,',','') /* Remove commas if exceeding 1000 */
set @percentage_beat1 = Subtract(@percentage1,100) /* Subtract 100 to get over/under */
set @percentage_beat1 = Replace(@percentage_beat1,'-','') /* Remove the '-' sign, if negative */
]%%
And as promised, here is the consolidated version, with all of the functions working in layers from the inside to the outside.
/* GOAL 3 */
set @actual3 = "421"
set @goal3 = "500"
set @percentage3 = Replace(FormatNumber(Multiply(Divide(@actual3,@goal3),100),'N0'),',','')
set @percentage_beat3 = Replace(Subtract(@percentage3,100),'-','')
Presenting The Results
Let’s evaluate those results to show a summary message. We’ve got 4 possible scenarios:
The goal was met at exactly 100% (
==is equal to)The goal was beat, over 100% (
>is greater than)The goal was missed, below 100% (
<is less than)The goal had no progress at all (
elsedefault)
So let’s hit some if/then statements:
if @percentage1 == 100 then
set @result1 = 'You met your goal – great work!'
elseif @percentage1 > 100 then
set @result1 = Concat('You beat your goal by ',@percentage_beat1,'% – nice work!')
elseif @percentage1 < 100 then
set @result1 = Concat('You missed your goal by ',@percentage_beat1,'% – try again next year!')
else
set @result1 = 'You may not have worked toward this goal this year, but try again next year!'
endif
This allows the summary sentence about each goal’s progress to be dynamic based on their results.
Dynamic HTML Progress Bars
Just for fun, let’s briefly play with one of my favorite AMPscript concepts — using AMPscript inside HTML/CSS. Since we have variables with percentages, we can use those variables for percentage-based HTML table cell widths to create a status bar that is partially or fully colored based on goal progress.
Here’s a picture of my proof of concept to help you visualize what we’re building:
First, we’ll evaluate the goal progress to create variable colors and widths.
If it’s at/above 100, then we’ll:
- Apply a green color
- Set the width of the complete bar to an even 100
- Set the width of the incomplete bar to 0, and hide it later
If it’s below 100, then we’ll:
- Apply a blue color
- Set the width of the complete bar to the actual progress percentage
- Set the width of the incomplete bar to the remainder (100 minus progress)
%%[
/* STATUS BARS */
if @percentage1 >= 100 then
set @percentage1_color = '#009933'
set @percentage1_complete_width = 100
set @percentage1_incomplete_width = 0
else
set @percentage1_color = '#0099ff'
set @percentage1_complete_width = @percentage1
set @percentage1_incomplete_width = Subtract(100,@percentage1)
endif
if @percentage2 >= 100 then
set @percentage2_color = '#009933'
set @percentage2_complete_width = 100
set @percentage2_incomplete_width = 0
else
set @percentage2_color = '#0099ff'
set @percentage2_complete_width = @percentage2
set @percentage2_incomplete_width = Subtract(100,@percentage2)
endif
]%%
Now let’s put those variables into some HTML. Notice the td cells are being populated with dynamic width, bgcolor, and background values.
<table width="600" align="left" cellspacing="0" cellpadding="0" border="0">
<tr>
<td width="600" align="left">
<br />Goal 1: %%=v(@actual1)=%% vs. %%=v(@goal1)=%% = %%=v(@percentage1)=%%%
<br />--> %%=v(@result1)=%%
</td>
</tr>
<tr>
<td width="600">
<table width="600" align="left" cellspacing="0" cellpadding="0" border="0">
<tr>
<td width="%%=v(@percentage1_complete_width)=%%%" align="right">
%%=v(@percentage1)=%%%
<div style="font-size: 6px; mso-line-height-rule: exactly; line-height: 6px;"> </div>
</td>
%%[if @percentage1_incomplete_width > 0 then]%%
<td width="%%=v(@percentage1_incomplete_width)=%%%"></td>
%%[endif]%%
</tr>
<tr>
<td width="%%=v(@percentage1_complete_width)=%%%" bgcolor="%%=v(@percentage1_color)=%%" style="background: %%=v(@percentage1_color)=%%;">
<div style="font-size: 18px; mso-line-height-rule: exactly; line-height: 18px;"> </div>
</td>
%%[if @percentage1_incomplete_width > 0 then]%%
<td width="%%=v(@percentage1_incomplete_width)=%%%" bgcolor="#bbbbbb" style="background: #bbbbbb;">
<div style="font-size: 18px; mso-line-height-rule: exactly; line-height: 18px;"> </div>
</td>
%%[endif]%%
</tr>
</table>
</td>
</tr>
<tr>
<td width="600">
<div style="font-size: 18px; mso-line-height-rule: exactly; line-height: 18px;"> </div>
</td>
</tr>
<tr>
<td width="600" align="left">
<br />Goal 2: %%=v(@actual2)=%% vs. %%=v(@goal2)=%% = %%=v(@percentage2)=%%%
<br />--> %%=v(@result2)=%%
</td>
</tr>
<tr>
<td width="600">
<table width="600" align="left" cellspacing="0" cellpadding="0" border="0">
<tr>
<td width="%%=v(@percentage2_complete_width)=%%%" align="right">
%%=v(@percentage2)=%%%
<div style="font-size: 6px; mso-line-height-rule: exactly; line-height: 6px;"> </div>
</td>
%%[if @percentage2_incomplete_width > 0 then]%%
<td width="%%=v(@percentage2_incomplete_width)=%%%"></td>
%%[endif]%%
</tr>
<tr>
<td width="%%=v(@percentage2_complete_width)=%%%" bgcolor="%%=v(@percentage2_color)=%%" style="background: %%=v(@percentage2_color)=%%;">
<div style="font-size: 18px; mso-line-height-rule: exactly; line-height: 18px;"> </div>
</td>
%%[if @percentage2_incomplete_width > 0 then]%%
<td width="%%=v(@percentage2_incomplete_width)=%%%" bgcolor="#bbbbbb" style="background: #bbbbbb;">
<div style="font-size: 18px; mso-line-height-rule: exactly; line-height: 18px;"> </div>
</td>
%%[endif]%%
</tr>
</table>
</td>
</tr>
</table>
Disclaimer: This is a loose proof of concept. It’s far from elegant HTML, but it shows the rough idea.
And here is our visual again of how it comes together:
Using this concept, there is a lot more you could do here to make it more elegant:
- Separate bars for goal vs. actual
- Numbers displayed inside the bars
- Repeating background images or possibly even an animated GIF to add texture/creative to the bars
However you want your design to look, blending AMPscript with HTML/CSS allows dynamic colors and sizes of elements with nothing but code.
I hope you found this helpful — if you have additional ways you’ve used AMPscript for percentages or dynamic email design, leave them in the comments!

Top comments (0)