When you use forEach like this, you are creating completely independent sheet workers, one for each stat in your stats array. When any of those stats change, everything in your setAttrs will run. So you'll have a sheet worker like this: on(`change:modifier_trainer_str`, () => { getAttrs(['modifier_trainer_str'], values => { setAttrs({ trainer_initiative: modifier_trainer_dex, trainer_sav_str: modifier_trainer_str, trainer_sav_dex: modifier_trainer_dex, trainer_sav_con: modifier_trainer_con, trainer_sav_int: modifier_trainer_int, trainer_sav_wis: modifier_trainer_wis, trainer_sav_cha: modifier_trainer_cha, trainer_skill_acrobatics: modifier_trainer_dex, trainer_skill_animalhandling: modifier_trainer_wis, trainer_skill_arcana: modifier_trainer_int, trainer_skill_athletics: modifier_trainer_str, trainer_skill_deception: modifier_trainer_cha, trainer_skill_history: modifier_trainer_int, trainer_skill_insight: modifier_trainer_wis, trainer_skill_intimidation: modifier_trainer_cha, trainer_skill_investigation: modifier_trainer_int, trainer_skill_medicine: modifier_trainer_wis, trainer_skill_nature: modifier_trainer_int, trainer_skill_perception: modifier_trainer_wis, trainer_skill_performance: modifier_trainer_cha, trainer_skill_persuasion: modifier_trainer_cha, trainer_skill_religion: modifier_trainer_int, trainer_skill_sleightofhand: modifier_trainer_dex, trainer_skill_stealth: modifier_trainer_dex, trainer_skill_survival: modifier_trainer_wis }); }); }); then another like this: on(`change:modifier_trainer_dex`, () => { getAttrs(['modifier_trainer_dex'], values => { setAttrs({ trainer_initiative: modifier_trainer_dex, trainer_sav_str: modifier_trainer_str, trainer_sav_dex: modifier_trainer_dex, trainer_sav_con: modifier_trainer_con, trainer_sav_int: modifier_trainer_int, trainer_sav_wis: modifier_trainer_wis, trainer_sav_cha: modifier_trainer_cha, trainer_skill_acrobatics: modifier_trainer_dex, trainer_skill_animalhandling: modifier_trainer_wis, trainer_skill_arcana: modifier_trainer_int, trainer_skill_athletics: modifier_trainer_str, trainer_skill_deception: modifier_trainer_cha, trainer_skill_history: modifier_trainer_int, trainer_skill_insight: modifier_trainer_wis, trainer_skill_intimidation: modifier_trainer_cha, trainer_skill_investigation: modifier_trainer_int, trainer_skill_medicine: modifier_trainer_wis, trainer_skill_nature: modifier_trainer_int, trainer_skill_perception: modifier_trainer_wis, trainer_skill_performance: modifier_trainer_cha, trainer_skill_persuasion: modifier_trainer_cha, trainer_skill_religion: modifier_trainer_int, trainer_skill_sleightofhand: modifier_trainer_dex, trainer_skill_stealth: modifier_trainer_dex, trainer_skill_survival: modifier_trainer_wis }); }); }); and so on for every stat in your array. (with the values. part added, I didn't want to manually add them all). As Scott says, you'll get a lot of undefined entries, because each worker doesn't have all the stats in the setAttrs. So you need to do one of two things: Use a single sheet worker, which includes a change: for every stat in your array. That way your setAttrs will work fine. Have a separate sheet worker for each of the core stats (modifier_trainer_str, modifier_trainer_dex, etc) and where the setAttrs includes only the values that are linked to that stat. It's possible to set this up programmatically. Converting it to a single sheet worker is easiest (requires least work to modify existing code, and it's my bed time so that's the one I'm doing), and that would look like: const stats = [ "modifier_trainer_str" , "modifier_trainer_dex" , "modifier_trainer_con" , "modifier_trainer_int" , "modifier_trainer_wis" , "modifier_trainer_cha" ]; const stats_changes = stats . map ( stat => `change: ${ stat } ` ). join ( ' ' ); // build a string with all the change: entries, because I am too sleepy to type them. on ( stats_changes , () => { getAttrs ( stats , values => { setAttrs ({ trainer_initiative : values . modifier_trainer_dex , trainer_sav_str : values . modifier_trainer_str , trainer_sav_dex : values . modifier_trainer_dex , trainer_sav_con : values . modifier_trainer_con , trainer_sav_int : values . modifier_trainer_int , trainer_sav_wis : values . modifier_trainer_wis , trainer_sav_cha : values . modifier_trainer_cha , trainer_skill_acrobatics : values . modifier_trainer_dex , trainer_skill_animalhandling : values . modifier_trainer_wis , trainer_skill_arcana : values . modifier_trainer_int , trainer_skill_athletics : values . modifier_trainer_str , trainer_skill_deception : values . modifier_trainer_cha , trainer_skill_history : values . modifier_trainer_int , trainer_skill_insight : values . modifier_trainer_wis , trainer_skill_intimidation : values . modifier_trainer_cha , trainer_skill_investigation : values . modifier_trainer_int , trainer_skill_medicine : values . modifier_trainer_wis , trainer_skill_nature : values . modifier_trainer_int , trainer_skill_perception : values . modifier_trainer_wis , trainer_skill_performance : values . modifier_trainer_cha , trainer_skill_persuasion : values . modifier_trainer_cha , trainer_skill_religion : values . modifier_trainer_int , trainer_skill_sleightofhand : values . modifier_trainer_dex , trainer_skill_stealth : values . modifier_trainer_dex , trainer_skill_survival : values . modifier_trainer_wis }); }); }); This does raise the question: are those skills modified by anything else, like level, proficiency, etc. If so this above method is incomplete and you'll need further work to account for such things. maybe these are sent to an attribute bonus input for that skill.