Thanks, GiGs...I meant to include:  setAttrs(sattrs,{silent:true});  Because I'd tried that as well. Silent doesn't seem to be doing anything...or rather it still triggers.  Anyway, to get us all on the same page, I modified my code to match the above. To help (I hope), I'm also going to include the HTML portion  sheet worker:  //Create Trait values for rank and display hidden fields  	 const traits = ['agility','smarts','spirit','strength','vigor','fighting','academicsskill','athletics','battle','boating','climbing','commonknowledge','driving','electronics','faith','focus','gambling','guts','hacking','healing','intimidation','investigation','language','lockpicking','notice','occult','performance','persuasion','piloting','psionics','repair','research','riding','ritual','science','shooting','spellcasting','stealth','streetwise','survival','swimming','taunt','throwing','thievery','tracking','weirdscience','repeating_skills:skillnamerank','pastrength','rastrength'];  	  traits.forEach(trait => { //Should trigger second   	      on(`change:${trait} change:${trait}mod`, function(eventInfo)  {      	     console.log(`+++ Change Detected for ${trait} +++`);      	     console.log("Trait triggering update: " + eventInfo.sourceAttribute);  	         //let trait = traits;  	         console.log("trait = "+trait);  	         console.log("Check for repeating: "+trait.slice(0,6));  	         /*if (trait.slice(0,5) === 'repeating') {  	             traitcheck = `repeating_skills_${traits}`;  	         }*/  	         trait = trait.replace(':','_');  	         console.log("trait = "+trait);  	         getAttrs([trait, `${trait}mod`, `${trait}_delta`], v => {  	             console.log("intMod will == "+v[`${trait}mod`]);                 console.log("Delta will == "+v[`${trait}_delta`]);                 let tDelta = v[`${trait}_delta`];                 tDelta = parseInt(tDelta)||0;                 let tDie = parseInt(v[trait])||0;                 console.log(`^^^ Delta: ${tDelta}, tDie: ${tDie} ^^^`);                 let tRank = '1d';                 let tDisplay = (tDelta !== 0 ? "*d" : "d");  	             let intMod = v[`${trait}mod`];                 intMod = parseInt(intMod)||0;  	             tRank += v[trait]+'!';  	             tDisplay += v[trait];  	             if(intMod > 0) {                     console.log("Mod is > 0...use +");      				 tRank += '+'+intMod+'[trait]';      				 tDisplay += '+' + intMod;      			 }                 else if(intMod < 0) {                     console.log("Mod is < 0...use -");      				 tRank += intMod+'[trait]';      				 tDisplay += intMod;                 }                 else {console.log("Mod is 0...don't do anything");}  	             const sAttRank = trait + '_rank', sAttDisplay = trait + '_display';      			 const sattrs = {};      			 sattrs[sAttRank]=tRank;      			 sattrs[sAttDisplay]=tDisplay;      			 setAttrs(sattrs);  	         });  	     });  	 });     //Do the Delta thing here...     //Set the actual trait and trait mod fields...this is needed so things like Vigor and Parry update if the appropriate trait is boosted/lowered      traits.forEach(trait => {         on(`change:${trait}_delta`, function(eventInfo) {              //let trait = traits;             console.log(`^o^ Delta changed for ${trait} ^o^`);             trait = trait.replace(':','_');  	         console.log("trait = "+trait);             getAttrs([trait, `${trait}_old`, `${trait}mod`, `${trait}_delta`], v => {                 let tRank = parseInt(v[`${trait}`])||0;                 let tMod = parseInt(v[`${trait}mod`])||0;                 let tDelta = parseInt(v[`${trait}_delta`])||0;                 let newtDelta, oldRankVal;                 (tDelta === 0 ? newtDelta = tDelta - eventInfo.previousValue : newtDelta = tDelta);                 console.log(`^^^ ${trait} Rank: ${tRank}, Mod: ${tMod}, Delta: ${tDelta}, New Delta: ${newtDelta} ^^^`);                 //Is the Delta something other than 0                 if(newtDelta !== 0) {                     let counter = Math.abs(newtDelta);                     if(newtDelta > 0) {                         //Increase                         console.log(`--- Increasing ${trait} ---`);                         if(tRank === 4 && tMod === -2) { //Check if skill is unskilled                             //unskilled                             counter = counter - 1;                             tMod = 0;                         }                         for(let i = counter; i > 0; i--) {                             if(tRank === 12) {                                 tMod += 1;                             }                             else {                                 tRank += 2;                             }                         }                     }                     else {                         //Decrease                         console.log(`--- Decreasing ${trait} ---`);                         if(tRank === 4) { //Rank is already 4 before decreasing...don't allow this                             tDelta = 0;                         }                         else { //Rank was not 4 before decreasing...                             for(let i = counter; i > 0; i--) {                                 if(tRank === 4) {                                     console.log(`Die Type is already 4, can't go any lower`);                                     tRank = tRank;                                 }                                 else {                                     if(tRank === 12 && tMod > 0) {                                         //Something like Professional or Expert Edge, need to reduce positive modifier first                                         tMod = tMod - 1;                                     }                                     else {                                         //No positive modifier on rank, so reduce the die type                                         tRank = tRank - 2;                                     }                                 }                             }                         }                     }                 }                 const sAttRank = trait, sAttMod = trait + 'mod', sAttDelta = `${trait}_delta`;      			 let sattrs = {};                 sattrs[sAttDelta]=tDelta;                 setAttrs(sattrs,{silent:true});                 sattrs = {};      			 sattrs[sAttRank]=tRank;      			 sattrs[sAttMod]=tMod;      			 setAttrs(sattrs);                              });         });     });  There are actually two sheet workers which use the same const array. I bolded the key lines where each start, just to make it a little clearer to see.  The first array (the one that would trigger most frequently) doesn't key off a change to delta, and the second only triggers on a change to delta.  In the second, that's also why I do two setAttrs calls, so I can set silently the delta trait before triggering the other changes that would trigger the first sheet worker.  And here's the HTML:  <div class='sheet-basetrait'>
	<input class="sheet-deltabutton" type="hidden" name="attr_agility_delta" value="0" />
	<button class="sheet-traitbutton sheet-traitflexattbutton" type='roll' name='roll_tAgilityRoll' title="@{tAgilityRoll}" value='@{agilitybutton}' data-i18n="agility"> Agility</button>
	<input type="hidden" name="attr_agilitybutton" value="@{whisperagility} &{template:roll} @{rolltAgility} @{setmodsenc} @{showtraitmods}" />
	<span><select name="attr_agility" title="@{agility}" class="sheet-traitdie" value="4">
		<option value="4">d4</option>
		<option value="6">d6</option>
		<option value="8">d8</option>
		<option value="10">d10</option>
		<option value="12">d12</option>
	</select>+</span>
	<input class="sheet-otraitmod" type="number" name="attr_agilitymod" title="@{agilitymod}" value="0" />
	<input class='sheet-moremods' type='checkbox' name='attr_showAgMod' /><span>y</span>
	<div class='sheet-basetrait sheet-traitmodplus'>
		<hr class="sheet-modsection">
		<input class='sheet-whispercheck' type='checkbox' value='/w gm' name='attr_whisperagility' /> <span class='sheet-ModDesc' data-i18n="whisper-roll-to-gm">Whisper roll to GM</span><br />
		<select name="attr_agwilddie" title="@{agwilddie}" class="sheet-traitdie" value="6">
			<option value="4">d4</option>
			<option value="6">d6</option>
			<option value="8">d8</option>
			<option value="10">d10</option>
			<option value="12">d12</option>
		</select>
		<input type='text' class='sheet-ModDesc' name='attr_agwilddieinfo' placeholder='Standard Wild Die die type' data-i18n-placeholder="standard-wild-die-type" /><br />
		<hr class="sheet-modsection">
		<input type='text' class='sheet-ModDesc' name='attr_agModType' placeholder='Modifier due to edges, hindrances, etc' data-i18n-placeholder="att-mod-description" />
		+<input class="sheet-otraitmod" type="number" name="attr_agrollMod" title="@{agrollMod}" value="0" /><br />
		<label class="sheet-delta" data-i18n="die-step-delta">Increase/Decrease die type by number of steps</label><input class="sheet-otraitmod" style="border-bottom: 1px solid black;" type="number" name="attr_agility_delta" title="@{agility_delta}" value="0" />
	</div>
	<input type='hidden' name='attr_agility_rank' value='1d4!' />
	<input type='hidden' name='attr_agility_display' value='1d4' />
	<input type='hidden' name='attr_rolltAgility' value='{{name=@{character_name} }} {{trait=^{agility}}} {{skill_rank=@{agility_display} }} {{skill_roll=[[@{agility_rank} + @{agrollMod}+ @{ModSumEnc}[Wounds, Fatigue, Encumbrance, Distracted] + @{traitmods} ]] }} {{mook=[[1d0+@{wdNum}]]}} {{wild_die=[[ 1d@{agwilddie}! + @{agilitymod} + @{agrollMod} + @{ModSumEnc}[Wounds, Fatigue, Encumbrance] + @{traitmods} ]]}}' />
</div>