This tool allows you to quickly build your weightlifting programs, ensure you have proper weekly volume per muscle group , and balance it with the time you spend in a gym . You can build multi-week programs, plan your mesocycles, deload weeks, testing 1RM weeks, and see the weekly undulation of volume and intensity of each exercise on a graph.
Set the program name, create weeks and days, type the list of exercises for each day, putting each exercise on a new line, along with the number of sets and reps after slash (
/ ) character, like this:
Squat 3x3-5
Romanian Deadlift 3x8
Autocomplete will help you with the exercise names. You can also create custom exercises if they're missing in the library.
On the right you'll see Weekly Stats , where you can see the number of sets per week per muscle group, whether you're in the recommended range (indicated by color), strength/hypertrophy split, and if you hover a mouse over the numbers - you'll see what exercises contribute to that number, and how much.
The exercise syntax supports RPEs , percentage of 1RM , rest timers, various progressive overload types, etc. Read more about the features in the docs !
When you're done, you can convert this program to Liftosaur program, and run what you planned in the gym, using the Liftosaur app !
To use this program:
Install Liftosaur app Copy the link to this program by clicking on below Import the link in the app, on the Choose Program screen. Delete Week Add New Week Duplicate Week Add Week Description
Day Description (Markdown)
Delete Day Description
Delete Day
Day Description (Markdown)
Delete Day Description
Delete Day
Day Description (Markdown)
Delete Day Description
Delete Day
Add Day Description
Delete Day
Add Day
Week Stats Total Sets: 78
Strength Sets: 36, 46%
Hypertrophy Sets: 42, 54%
Upper Sets: 30 (12s , 18h ), 2d Lower Sets: 24 (12s , 12h ), 2d Core Sets: 27 (12s , 15h ), 4d Push Sets: 12 (6s , 6h ), 2d Pull Sets: 18 (6s , 12h ), 2d Legs Sets: 21 (12s , 9h ), 2d Shoulders: 14↓ (8s , 6h ), 2d Glutes: 27↓ (15s , 12h ), 4d Hamstrings: 11 (6s , 5h ), 2d Quadriceps: 20↓ (14s , 6h ), 4d Forearms: 8↑ (3s , 5h ), 2d {"maxWidth":2400,"url":"/planner","isLoggedIn":false}
{"exportedProgram":{"customExercises":{"evlihmya":{"types":["upper","pull"],"isDeleted":false,"smallImageUrl":"https://liftmanual.com/wp-content/uploads/2023/04/suspension-fly.jpg","vtype":"custom_exercise","meta":{"synergistMuscles":["Biceps Brachii","Deltoid Anterior"],"targetMuscles":["Pectoralis Major Clavicular Head","Pectoralis Major Sternal Head"],"bodyParts":[],"sortedEquipment":[]},"largeImageUrl":"https://liftmanual.com/wp-content/uploads/2023/04/suspension-fly.jpg","name":"Fly, Suspension","id":"evlihmya"},"vzhtybfa":{"types":["upper","push"],"isDeleted":false,"smallImageUrl":"https://www.hevyapp.com/wp-content/uploads/TRX-.png","vtype":"custom_exercise","meta":{"synergistMuscles":["Rectus Abdominis"],"targetMuscles":["Triceps Brachii"],"bodyParts":[],"sortedEquipment":[]},"largeImageUrl":"https://www.hevyapp.com/wp-content/uploads/TRX-.png","name":"Tricep Extension, Suspension","id":"vzhtybfa"},"kishkqsp":{"types":["push","upper"],"isDeleted":false,"smallImageUrl":"https://www.liftosaur.com/userimages/user-uploads/vzsfdqpyey/fnehbmzh-kishkqsp.png","vtype":"custom_exercise","meta":{"synergistMuscles":["Brachialis","Brachioradialis","Latissimus Dorsi","Rectus Abdominis"],"targetMuscles":["Biceps Brachii"],"bodyParts":[],"sortedEquipment":[]},"name":"Pelican Curl","id":"kishkqsp"},"nkifuond":{"types":["core"],"isDeleted":false,"smallImageUrl":"https://workoutlabs.com/train/svg.php?id=85258","vtype":"custom_exercise","meta":{"synergistMuscles":["Adductor Longus","Adductor Brevis","Deltoid Posterior","Latissimus Dorsi","Pectineous","Pectoralis Major Sternal Head","Sartorius","Teres Major"],"targetMuscles":["Iliopsoas"],"bodyParts":[],"sortedEquipment":[]},"largeImageUrl":"https://workoutlabs.com/train/svg.php?id=85258","name":"Ab Rollout, Suspension","id":"nkifuond"},"txtcknjd":{"types":["core"],"isDeleted":false,"smallImageUrl":"https://sportivetricksstorage.blob.core.windows.net/images/articles/training/technique/palof-press/1-palof-press.webp","vtype":"custom_exercise","meta":{"synergistMuscles":["Gluteus Maximus","Gluteus Medius","Rectus Abdominis"],"targetMuscles":["Obliques"],"bodyParts":[],"sortedEquipment":[]},"largeImageUrl":"https://sportivetricksstorage.blob.core.windows.net/images/articles/training/technique/palof-press/1-palof-press.webp","name":"Palloff Press, Band","id":"txtcknjd"},"yyjyuhmg":{"types":["push","upper"],"isDeleted":false,"smallImageUrl":"https://www.endomondo.com/wp-content/uploads/2024/08/Tricep-Dumbbell-Kickback-Guide.png","vtype":"custom_exercise","meta":{"synergistMuscles":["Brachialis"],"targetMuscles":[],"bodyParts":[],"sortedEquipment":[]},"largeImageUrl":"https://kinxlearning.com/cdn/shop/files/exercise-47_1000x.jpg?v=1613157966","name":"Tricep Kickback, Dumbbell","id":"yyjyuhmg"},"ahrbpaoe":{"types":["core"],"isDeleted":false,"smallImageUrl":"https://sportivetricksstorage.blob.core.windows.net/images/articles/training/technique/palof-press/1-palof-press.webp","vtype":"custom_exercise","meta":{"synergistMuscles":["Gluteus Maximus","Gluteus Medius","Rectus Abdominis"],"targetMuscles":["Obliques"],"bodyParts":[],"sortedEquipment":[]},"largeImageUrl":"https://sportivetricksstorage.blob.core.windows.net/images/articles/training/technique/palof-press/1-palof-press.webp","name":"Palloff Press","id":"ahrbpaoe"},"rlsvfcef":{"types":["lower","legs"],"isDeleted":false,"smallImageUrl":"https://www.liftosaur.com/userimages/user-uploads/vzsfdqpyey/bcpalfrg-rlsvfcef.jpg","vtype":"custom_exercise","meta":{"synergistMuscles":["Erector Spinae","Gastrocnemius","Gluteus Maximus","Gluteus Medius","Obliques","Rectus Abdominis","Soleus"],"targetMuscles":["Hamstrings"],"bodyParts":[],"sortedEquipment":[]},"name":"Hamstring Curl, Slider","id":"rlsvfcef"},"iklajdpr":{"types":["core"],"isDeleted":false,"smallImageUrl":"https://liftmanual.com/wp-content/uploads/2023/04/hollow-hold.webp","vtype":"custom_exercise","meta":{"synergistMuscles":["Gluteus Maximus","Gluteus Medius","Obliques","Sartorius","Tensor Fasciae Latae"],"targetMuscles":["Rectus Abdominis"],"bodyParts":[],"sortedEquipment":[]},"largeImageUrl":"https://liftmanual.com/wp-content/uploads/2023/04/hollow-hold.jpg","name":"Hollow Hold","id":"iklajdpr"},"uawgwehb":{"types":[],"isDeleted":false,"smallImageUrl":"https://fitnessprogramer.com/wp-content/uploads/2021/06/Bench-Pike-Push-up.gif","vtype":"custom_exercise","meta":{"synergistMuscles":["Deltoid Lateral","Triceps Brachii","Serratus Anterior","Pectoralis Major Clavicular Head"],"targetMuscles":["Deltoid Anterior"],"bodyParts":[],"sortedEquipment":[]},"largeImageUrl":"https://liftmanual.com/wp-content/uploads/2023/04/pike-push-up.jpg","name":"Pike Pushup","id":"uawgwehb"}},"program":{"deletedExercises":[],"planner":{"name":"2025 Automatic for the People V4","vtype":"planner","weeks":[{"name":"Gym","days":[{"name":"Horizontal","exerciseText":"/// --------------------------------------------------------------------------------\n/// Blank slate will have just minreps and max reps.\n\n// Core Compound Movements\nBench Press, Barbell / ...Compound[3:1] / progress: custom(minReps: 5, maxReps: 9, targetReps: 9, maxvolume: 4200lb, maxweight: 185lb, liftsincedeload: 5, liftsince1rm: 1) { ...Compound }\nBent Over Row, Barbell / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 10, targetReps: 7, maxvolume: 3615lb, maxweight: 140lb, liftsincedeload: 4) { ...Compound }\n\n// Accessory Lifts\n// Shoulders\nReverse Fly, Band / used: none / 3x8-12 / @9+ 90s / warmup: none / superset: Accessory\nFace Pull, Band / warmup: none / ...Accessory[3:1] / superset: Accessory\nFace Pull, Cable / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxvolume: 2502.5lb, maxweight: 67.5lb, liftsincedeload: 3) { ...Accessory }\n\n// Triceps\nTriceps Dip, Leverage Machine / used: none / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 10, maxReps: 15) { ...Accessory }\nTriceps Extension, Dumbbell / used: none / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxweight: 20lb, liftsincedeload: 4, maxvolume: 760, liftsince1rm: 2, liftsinceprogress: 2) { ...Accessory }\nTricep Kickback, Dumbbell / used: none / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxweight: 25lb, maxvolume: 825lb, liftsincedeload: 2, liftsince1rm: 1, liftsinceprogress: 1) { ...Accessory }\nTriceps Pushdown, Cable / used: none / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxweight: 20lb) { ...Accessory }\nSkullcrusher, EZ Bar / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 8, maxReps: 12) { ...Accessory }\n\n// Chest\nPec Deck / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxvolume: 5997.5lb, maxweight: 205lb, liftsincedeload: 3) { ...Accessory }\n\n// Core (pick one)\nPalloff Press / 3x7-12 / @9 90s / warmup: none / update: custom() { ...Accessory } / progress: custom(minReps: 7, maxReps: 12, maxweight: 47.5lb, liftsincedeload: 5, maxvolume: 1591lb) { ...Accessory }\nHollow Hold / 1x1 47lb+, 1x1 41lb+, 1x1 35lb+ / 90s / warmup: none / progress: lp(5lb, 3, 2, 10%)","description":"Horizontal push and pull + core"},{"name":"Leg Day 1","exerciseText":"// Core Compound Movements\nTrap Bar Deadlift, Trap Bar / ...Compound[3:1] / progress: custom(minReps: 5, maxReps: 7, targetReps: 6, maxvolume: 6084lb, maxweight: 313lb, liftsincedeload: 4, liftsince1rm: 1) { ...Compound }\nBulgarian Split Squat, Dumbbell / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 10, targetReps: 10, maxvolume: 1519lb, maxweight: 67.5lb, liftsincedeload: 5, liftsince1rm: 3) { ...Compound }\n\n// Accessory Lifts\nReverse Hyperextension, Leverage Machine / ...Accessory[3:1] / warmup: none / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxvolume: 2700lb, maxweight: 75lb, liftsincedeload: 3, liftsince1rm: 1, liftsinceprogress: 1) { ...Accessory }\nLeg Press / ...Accessory[3:1] / warmup: none / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxvolume: 5730lb, maxweight: 170lb, liftsincedeload: 4) { ...Accessory }\n\nHip Thrust, Barbell / used: none / ...Compound[3:1] / progress: custom(minReps: 8, maxReps: 10, targetReps: 9, maxvolume: 3815lb, maxweight: 145lb, liftsince1rm: 1, liftsincedeload: 1) { ...Compound }\n\n// Core (pick one)\nPalloff Press / ...Palloff Press[1:1]\nHollow Hold / ...Hollow Hold[1:1] / 1x1 47lb, 1x1 41lb, 1x1 35lb","description":"Deadlift-centered Leg Day"},{"name":"Vertical","exerciseText":"// Core Compound Movements\nPull Up / ...Compound[3:1] / progress: custom(minReps: 5, maxReps: 10, targetReps: 7, maxvolume: 5775lb, maxweight: 235.2lb, bodyweight: 1, liftsincedeload: 8, liftsince1rm: 3, liftsinceprogress: 0, accessory: 0) { ...Compound }\n\nShoulder Press, Dumbbell / ...Compound[3:1] / progress: custom(minReps: 5, maxReps: 7, targetReps: 5, maxvolume: 1013.5lb, maxweight: 55lb, liftsincedeload: 7, liftsinceprogress: 0, bodyweight: 0, accessory: 0) { ...Compound }\n\n// Accessory Lifts\nShrug, Barbell / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxvolume: 7600lb, maxweight: 240lb, liftsincedeload: 9, liftsince1rm: 3, liftsinceprogress: 3) { ...Accessory }\nBicep Curl, Dumbbell / used: none / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 8, maxReps: 12, maxvolume: 1155lb, maxweight: 37.5lb, liftsince1rm: 2, liftsincedeload: 2, liftsinceprogress: 2, increment: 5lb, bodyweight: 0) { ...Accessory }\nPreacher Curl, Dumbbell / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 7, maxReps: 10) { ...Accessory }\n\n// Core (pick one)\nPalloff Press / ...Palloff Press[1:1]\nHollow Hold / ...Hollow Hold[1:1] / 1x1 47lb, 1x1 41lb, 1x1 35lb","description":"Vertical Push and"},{"name":"Leg Day 2","exerciseText":"// Core Compound Movements\nSquat, Barbell / ...Compound[3:1] / progress: custom(minReps: 6, maxReps: 9, targetReps: 7, maxvolume: 2565lb, maxweight: 115lb, liftsincedeload: 3, increment: 5lb, liftsince1rm: 1) { ...Compound }\n\nRomanian Deadlift, Barbell / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 10, targetReps: 9, maxvolume: 4630lb, maxweight: 180lb, liftsince1rm: 3, liftsincedeload: 3, increment: 5lb) { ...Compound }\n\n// Accessory Lifts\nStanding Calf Raise, Dumbbell / ...Accessory[3:1] / superset: Accessory / progress: custom(minReps: 10, maxReps: 15, maxvolume: 25080lb, maxweight: 61lb, bodyweight: 1, liftsince1rm: 3, liftsinceprogress: 3, liftsincedeload: 2) { ...Accessory }\n\nLeg Press / ...Accessory[3:1]\n\n// Core (pick one)\nPalloff Press / ...Palloff Press[1:1]\nHollow Hold / ...Hollow Hold[1:1] / 1x1 47lb, 1x1 41lb, 1x1 35lb"}]},{"name":"Home","days":[{"name":"Horizontal","exerciseText":"\n// Core Compound Movements\nChest Dip / ...Compound[3:1] / progress: custom(minReps: 5, maxReps: 8, targetReps: 6, maxvolume: 4543.2lb, maxweight: 221.9lb, liftsincedeload: 6, bodyweight: 1, liftsince1rm: 2) { ...Compound }\n\nPush Up, Band / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 10, targetReps: 9, maxvolume: 2610lb, maxweight: 100lb, bodyweight: 0.7, liftsincedeload: 7) { ...Compound }\n\nInverted Row / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 12, targetReps: 9, bodyweight: 0.7, liftsincedeload: 7, maxweight: 15lb, maxvolume: 372.5lb) { ...Compound }\n\n// Accessory Lifts\n// Shoulders\nFly, Suspension / 3x7-12 / @9+ 90s / warmup: none / superset: Accessory\n// Accessory Lifts\n// Shoulders\nReverse Fly, Band / used: none / 3x7-12 / @9+ 90s / superset: Accessory\nFace Pull, Band / warmup: none / ...Accessory[3:1] / superset: Accessory\n// lp(weight increase, increase attempts, current increase attempt, weight decrease, decrease attempts, current decrease attempt)\nPush Up, Bodyweight / 2x10+ 1lb, 1x10+ / @10+ 90s / warmup: none / progress: lp(1lb, 3, 1, 1lb, 3, 1)\n\nHollow Hold / ...Hollow Hold[1:1] / 1x1 47lb, 1x1 41lb, 1x1 35lb\nTricep Extension, Suspension / used: none / 3x7-12 / @9+ 90s / warmup: none / superset: Accessory\nSkullcrusher, Dumbbell / warmup: none / ...Accessory[3:1] / superset: Accessory\n","description":"Horizontal push and pull"},{"name":"Legs","exerciseText":"// Core Compound Movements\nBulgarian Split Squat, Dumbbell / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 10, targetReps: 10, maxvolume: 1519lb, maxweight: 67.5lb, liftsincedeload: 5, liftsince1rm: 3) { ...Compound }\n\n// Accessory Lifts\nStanding Calf Raise, Dumbbell / ...Accessory[3:1] / superset: Accessory\n\nSingle Leg Deadlift, Band / ...Compound[3:1] / progress: custom(minReps: 5, maxReps: 8, targetReps: 5, maxvolume: 3805lb, maxweight: 180lb, liftsincedeload: 7) { ...Compound }\nSingle Leg Deadlift, Dumbbell / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 12) { ...Compound }\n\nPalloff Press, Band / 3x7-12 / @9 90s","description":"Unilateral Legs"},{"name":"Vertical","exerciseText":"Ab Wheel / ...Compound[3:1] / progress: custom(minReps: 5, maxReps: 9, targetReps: 6, maxvolume: 4237.5lb, maxweight: 184lb, bodyweight: 1, liftsince1rm: 3, liftsincedeload: 1, liftsinceprogress: 1, accessory: 0) { ...Compound }\nAb Rollout, Suspension / used: none / 3x10-15 / @9+ / warmup: none\n\n// Core Compound Movements\nPull Up / ...Compound[3:1]\nPike Pushup / used: none / 3x6-9 / @8+ 120s / warmup: 1x5 50%, 1x5 60%\nShoulder Press, Dumbbell / ...Compound[3:1] \n\n// Accessory Lifts\nBicep Curl, Dumbbell / ...Accessory[3:1] / superset: Accessory\nPelican Curl / used: none / 3x3-5 / @9+ 90s / warmup: none\n\nShrug, Band / ...Accessory[3:1] / warmup: none / superset: Accessory / progress: custom(minReps: 7, maxReps: 12, maxvolume: 5250lb, maxweight: 180lb, liftsincedeload: 9, liftsince1rm: 5, liftsinceprogress: 5, increment: 5lb, bodyweight: 0) { ...Accessory }"},{"name":"Leg Day II","exerciseText":"// Goblet squat with bands\nSpanish: Goblet Squat, Kettlebell / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 10, targetReps: 10, maxweight: 68.5lb, maxvolume: 1655.5lb, liftsincedeload: 5, liftsince1rm: 3) { ...Compound }\n\nReverse Lunge, Dumbbell / ...Compound[3:1] / progress: custom(minReps: 7, maxReps: 10, targetReps: 10, maxvolume: 549lb, maxweight: 22.5lb, liftsincedeload: 4, liftsince1rm: 3) { ...Compound }\n\nHamstring Curl, Slider / 3x7-12 / @9+ 90s / warmup: none / superset: Accessory\n\nPalloff Press, Band / 3x7-12 / @9 90s"}]},{"name":"Templates","days":[{"name":"Template Day","exerciseText":"/// --------------------------------------------------------------------------------\n/// Templates could be in a day of their own but that would clutter. Putting them first lets other things change, at a cost. Also lets them be used for adding exercises.\n/// --------------------------------------------------------------------------------\n/// --------------------------------------------------------------------------------\n/// T2 Template. Like T1 but asks for RPE and adjusts weights accordingly\n/// --------------------------------------------------------------------------------\n\nCompound / used: none / 2x5 @8+, 1x5 @9+ / 120s / update: custom() {~\n ///emplate is for main compound exercises. Rep ranges differ by lift. Less for overhead and hinge exercises. \n /// Originally had parametrized min reps and max reps via state variables, but cleaner to pass them with liftosaur syntax / ranges. That means extracting them from the minReps variable and the reps variable.\n\n state.targetReps = state.targetReps == 0 ? state.minReps : state.targetReps // protect against first run. \n\n /// --------------------------------------------------------------------------------\n /// Set stuff up before the start.\n if (setIndex == 0) {\n /// Set up sets. Timers, RPE / Log RPE come from configuration, not set in variables. \n var.i = 1\n for (var.r in reps) {\n reps[var.i] = state.targetReps\n weights[var.i] = rm1 * rpeMultiplier(reps[var.i],RPE[var.i]) // In general prefer spec'd RPE to target RPE; allows for set-to-set customization.\n /// sets(fromIndex, toIndex, minReps, maxReps, isAmrap, weight, timer, rpe, shouldLogRpe)\n var.i += 1\n }\n }\n\n /// -------------------------------------------------------------------------------- \n if (setIndex > 0 && setIndex < numberOfSets) {\n // Use volume-based success instead of \"meet both criteria\"; allows (roughly) success for nreps += 1 with less weight\n var.success = reps[setIndex] * weights[setIndex] <= completedReps[setIndex] * completedWeights[setIndex] \n \n /// Adjusts up or down based on what happened. Can be simpler if we get actual RPE\n var.e1RM = completedWeights[setIndex] / rpeMultiplier(completedReps[setIndex],completedRPE[setIndex]) \n\n if (!var.success) {\n timers[setIndex] +=30 /// Extra rest time if didn't meet goal\n }\n \n /// Adjust down based on time between sets and how close we were to failure. \"Decay\" of fatigue. Autoregulatory model\n \n var.RIR = 10-RPE[setIndex]\n var.k = timers[setIndex]\n var.decay = (var.k/(var.k+2)) /// Model roughly 8 minutes to full recovery\n var.decay *= (0.96 + 0.04 * var.RIR / (var.RIR + 0.67)) // Model decay based on how hard pushed too. Was .95+.05\n var.new_weight = var.decay *var.e1RM * rpeMultiplier(state.targetReps, RPE[setIndex+1])\n weights = var.new_weight /// Bit of a hack - don't loop since earlier sets can't change.\n\n // Now optimize the weights / reps combo to find the one that best matches the target volume (new weight). Recognizeds\n // That weights are quantized\n var.new_target_volume = var.new_weight * state.targetReps\n \n // Define the 3 Reps\n var.r1 = state.targetReps - 1;\n var.r2 = state.targetReps;\n var.r3 = state.targetReps + 1;\n \n // Define the RPE-Limited Maximum Weights (unrounded)\n var.maxW_r1_raw = var.decay * var.e1RM * rpeMultiplier(var.r1, RPE[setIndex+1]);\n var.maxW_r2_raw = var.decay * var.e1RM * rpeMultiplier(var.r2, RPE[setIndex+1]);\n var.maxW_r3_raw = var.decay * var.e1RM * rpeMultiplier(var.r3, RPE[setIndex+1]);\n \n // --- Candidate 1: Reps r1 (n-1) ---\n var.r_current = var.r1; \n \n // 1. Calculate raw weight: min(Volume_Target / Reps, Max_RPE_Weight)\n var.rawW1 = (var.new_target_volume / var.r_current) < var.maxW_r1_raw \n ? (var.new_target_volume / var.r_current) \n : var.maxW_r1_raw;\n \n // 2. Round raw weight to the closest available equipment weight\n var.w1 = roundWeight(var.rawW1);\n var.v1 = var.r_current * var.w1;\n \n // 3. Calculate absolute volume error\n var.e1 = (var.v1 - var.new_target_volume) >= 0 \n ? (var.v1 - var.new_target_volume) \n : (var.new_target_volume - var.v1);\n \n // --- Candidate 2: Reps r2 (n) ---\n var.r_current = var.r2;\n \n // 1. Calculate raw weight\n var.rawW2 = (var.new_target_volume / var.r_current) < var.maxW_r2_raw \n ? (var.new_target_volume / var.r_current) \n : var.maxW_r2_raw;\n \n // 2. Round raw weight\n var.w2 = roundWeight(var.rawW2);\n var.v2 = var.r_current * var.w2;\n \n // 3. Calculate absolute volume error\n var.e2 = (var.v2 - var.new_target_volume) >= 0 \n ? (var.v2 - var.new_target_volume) \n : (var.new_target_volume - var.v2);\n \n // --- Candidate 3: Reps r3 (n+1) ---\n var.r_current = var.r3;\n \n // 1. Calculate raw weight\n var.rawW3 = (var.new_target_volume / var.r_current) < var.maxW_r3_raw \n ? (var.new_target_volume / var.r_current) \n : var.maxW_r3_raw;\n \n // 2. Round raw weight\n var.w3 = roundWeight(var.rawW3);\n var.v3 = var.r_current * var.w3;\n \n // 3. Calculate absolute volume error\n var.e3 = (var.v3 - var.new_target_volume) >= 0 \n ? (var.v3 - var.new_target_volume) \n : (var.new_target_volume - var.v3);\n \n // --- Find the Best Candidate ---\n \n // Initialize the best candidate to r1/w1/e1\n var.bestReps = var.r1;\n var.bestWeight = var.w1;\n var.bestError = var.e1;\n \n // Compare best vs Candidate 2\n var.bestReps = var.e2 < var.bestError ? var.r2 : var.bestReps;\n var.bestWeight = var.e2 < var.bestError ? var.w2 : var.bestWeight;\n var.bestError = var.e2 < var.bestError ? var.e2 : var.bestError;\n \n // Compare best vs Candidate 3\n var.bestReps = var.e3 < var.bestError ? var.r3 : var.bestReps;\n var.bestWeight = var.e3 < var.bestError ? var.w3 : var.bestWeight;\n var.bestError = var.e3 < var.bestError ? var.e3 : var.bestError;\n\n /// print(var.r1,var.w1, var.v1, var.e1)\n /// print(var.r2,var.w2, var.v2, var.e2)\n /// print(var.r3,var.w3, var.v3, var.e3)\n \n // Optional: print for debugging\n // print(var.bestReps, var.bestWeight, var.bestError)\n \n // --- Final Output ---\n weights = var.bestWeight\n reps = var.bestReps\n \n }\n\n /// --------------------------------------------------------------------------------\n /// Now, if it's the last set ensure progress. If none, do a drop set. Use decay model for dropset too.\n if (setIndex == numberOfSets) { \n var.e1RM = completedWeights[setIndex] / rpeMultiplier(completedReps[setIndex],completedRPE[setIndex])\n var.RIR = 10-RPE[setIndex]\n var.k = timers[setIndex]\n var.decay = (var.k/(var.k+2)) /// Model roughly 8 minutes to full recovery\n var.decay *= (0.96 + 0.04 * var.RIR / (var.RIR + 0.67)) // Model decay based on how hard pushed too. Was .95+.05\n\n /// Compute (and update) volume; if volume < target volume, add a drop set at lower RPE. Note that we ask RPE in last.\n var.volume = 0\n var.target_volume = 0\n\n /// Analyze whether day was progress through planned sets. Note difference with progress above (hitting marks is not progress per se there)\n var.counter = 1\n for (var.set in completedWeights) {\n var.volume += completedWeights[var.counter] * completedReps[var.counter]\n if (var.counter <= programNumberOfSets) { // Calculate target volume in terms of program plan.\n var.target_volume += reps[var.counter] * weights[var.counter] // Note that this uses the rounded weights, not the \"originalWeights\", which would be an unfair penalty.\n }\n var.counter += 1\n }\n \n /// If didn't make at least one measure of progress do a dropset\n var.progress = var.target_volume*.99 < var.volume || state.maxvolume*.99 < var.volume // .99 is to deal with rounding errors on exact comparisons\n \n if (!var.progress) {\n //add a set\n numberOfSets += 1\n /// sets(fromIndex, toIndex, minReps, maxReps, isAmrap, weight, timer, rpe, shouldLogRpe)\n var.intensity = 7 // - state.liftsinceprogress\n sets(numberOfSets, numberOfSets, state.minReps, state.maxReps, 0, var.e1RM * var.decay * rpeMultiplier(state.targetReps,var.intensity), 120, var.intensity, 1) \n }\n \n /// Update state.maxvolume here, so it excludes any dropsets. \"Core\" volume since don't want to be locked into extra sets as the only way to progress volume.\n /// Note that this doesn't show if you click \"all sets at once\" in playground.\n state.maxvolume = state.maxvolume < var.volume ? var.volume : state.maxvolume\n }\n\n if (state.bodyweight > 0 && setIndex < numberOfSets) { \n print(weights[setIndex+1]-bodyweight)\n }\n\n /// Crude hack to implement deloading every time counter gets to 9x or week manually selected\n if (state.liftsincedeload >= 9) {\n weights = rm1 * .5\n timers = timers[1] *.6 // less rest\n }\n \n~} / progress: custom(targetReps: 0, minReps: 0, maxReps: 0, maxvolume: 0, maxweight: 0, liftsince1rm: 0, liftsincedeload: 0, liftsinceprogress: 0, bodyweight: 0, accessory: 0) {~\n /// Target weight preserved internally via 1rm variable.\n\n // Max volume should have been checked in the last set update, but it is not accessible in current progress run.\n // Update other state variables. This requires recomputation of progress. \n\n state.targetReps = state.targetReps == 0 ? state.minReps : state.targetReps // protect against first run. \n\n state.maxweight = state.maxweight <= max(completedWeights) ? max(completedWeights) : state.maxweight\n state.liftsincedeload += 1\n state.liftsince1rm += 1\n state.liftsinceprogress +=1 // asssume failure modes, decrement otherwise.\n\n /// var.e1RM = calculate1RM(completedWeights[setIndex], completedReps[setIndex])\n /// Analyze each set to see if overperformance implies an increase in 1RM\n for (var.set in reps) {\n var.RPE = completedRPE[var.set] > 0 ? completedRPE[var.set] : RPE[var.set] // Take entered RPE if available, if not, use the nominal target\n var.eRM1 = completedWeights[var.set] / rpeMultiplier(completedReps[var.set],var.RPE)\n var.delta = var.eRM1-rm1 \n if (var.delta / rm1 > .005) { // insist on a .5% change to avoid floating point problems in direct comparision.\n rm1 = var.eRM1\n state.liftsince1rm = 0\n /// state.targetReps = state.minReps // reset to bottom of rep range with weight increase\n state.liftsinceprogress = 0\n }\n } \n\n /// Compute (and update) volume; if volume < target volume, add a drop set at lower RPE. Note that we ask RPE in last.\n var.volume = 0\n var.target_volume = 0\n\n var.counter = 1\n for (var.set in completedWeights) {\n var.volume += completedWeights[var.counter] * completedReps[var.counter]\n var.target_volume += reps[var.counter] * weights[var.counter] // Note that this uses the rounded weights, not the \"originalWeights\", which would be an unfair penalty.\n var.counter += 1\n }\n \n /// If didn't make at least one measure of progress do a dropset\n var.progress = var.target_volume*.999 < var.volume || state.maxvolume*.99 < var.volume // .99 is to deal with rounding errors on exact comparisons\n\n //modified double progression. This below implements double progression. But modified b/c if a new PR is hit things reset that way too.\n if (var.progress && state.liftsince1rm > 0) { /// Met or volume targets and didn't otherwise increase weight\n state.liftsinceprogress = 0\n if (state.targetReps == state.maxReps){\n state.targetReps = state.minReps\n //weight increase carried in 1rm variable\n\n var.max_lift_multiplier = rpeMultiplier(state.minReps,RPE[1])\n var.old_max_lift = rm1 * var.max_lift_multiplier\n var.old_max_lift_rounded = roundWeight(var.old_max_lift)\n var.next_increment = increment(var.old_max_lift)\n\n var.newrm1 =rm1 * 1.035 // Increase should be 2-5%\n\n if (var.old_max_lift_rounded >= roundWeight(var.newrm1*var.max_lift_multiplier)) {\n // New target at minreps is not available as a weight increment. Forice that increment.\n var.newrm1 = var.next_increment / var.max_lift_multiplier\n } \n \n rm1 = var.newrm1\n state.liftsince1rm = 0\n } else {\n state.targetReps +=1\n }\n }\n\n /// Note that if it = 9 above it would be > 9 here as it was incremented\n if (state.liftsincedeload > 9) {\n state.liftsincedeload = 0\n state.liftsince1rm -= 1\n state.liftsinceprogress -=1 // Un do previous increment\n } else {\n /// Generalized, very slow decay of 1RM. Only way it is changed. One could imagine it beingset from test sets or paying attention to RPE\n /// But this is more automated. Ratch up, decay down.\n if (!var.progress && state.liftsince1rm > 0) {\n rm1 = rm1 * .995\n }\n }\n\n~}\n\n\n/// --------------------------------------------------------------------------------\n/// Accessory\n/// --------------------------------------------------------------------------------\n\nAccessory / used: none / 2x10-12 @9+, 1x10-12 @10+ / 90s / warmup: none / update: custom() {~\n /// Template for isolation exercises. Works with a rep range and target weight set in middle of it. Asks RPE always.\n\n /// --------------------------------------------------------------------------------\n if (setIndex == 0) {\n /// Set up sets\n var.i = 1\n for (var.r in reps) {\n var.targetReps = floor(state.minReps+(state.maxReps - state.minReps)/2)\n // sets(var.i, var.i, minReps[var.i], reps[var.i], 1, rm1 * rpeMultiplier(var.targetReps,RPE[var.i]) , var.restTime, RPE[var.i], 1) /// Could be minreps in multiplier\n sets(var.i, var.i, state.minReps, state.maxReps, 1, ceil(rm1 * rpeMultiplier(var.targetReps,RPE[var.i])) , timers[1], RPE[var.i], 1) /// Could be minreps in multiplier\n var.i += 1\n }\n }\n\n /// -------------------------------------------------------------------------------- \n if (setIndex > 0 && setIndex < numberOfSets) {\n /// var.success = completedReps[setIndex] >= minReps[setIndex] && completedWeights[setIndex] >= weights[setIndex]\n var.e1RM = completedWeights[setIndex] / rpeMultiplier(completedReps[setIndex],completedRPE[setIndex]) \n\n /// Adjust down based on time between sets and how close we were to failure. \"Decay\" of fatigue.\n /// This only models one set back, not cummulative. Could change e1RM logic above to have an effective decay (var.actual). But runs into rounding issues on weights and may decay too fast.\n var.RIR = 10-RPE[setIndex]\n var.k = timers[setIndex]\n var.decay = (var.k/(var.k+2)) /// Model roughly 8 minutes to full recovery\n var.decay *= (0.96 + 0.04 * var.RIR / (var.RIR + 0.67)) // Model decay based on how hard pushed too. Was .95+.05\n var.targetReps = floor(minReps[setIndex]+(reps[setIndex] - minReps[setIndex])/2)\n weights = var.decay *var.e1RM * rpeMultiplier(var.targetReps, RPE[setIndex])\n }\n\n /// --------------------------------------------------------------------------------\n /// Now, if it's the last set ensure progress. If none, do a drop set. Use decay model for dropset too.\n /// --------------------------------------------------------------------------------\n if (setIndex == numberOfSets) { \n /// Compute (and update) volume; no dropsets in isloation exercises\n var.e1RM = completedWeights[setIndex] / rpeMultiplier(completedReps[setIndex],completedRPE[setIndex])\n var.RIR = 10-RPE[setIndex]\n var.k = timers[setIndex]\n var.decay = (var.k/(var.k+2)) /// Model roughly 8 minutes to full recovery\n var.decay *= (0.96 + 0.04 * var.RIR / (var.RIR + 0.67)) // Model decay based on how hard pushed too. Was .95+.05\n var.targetReps = floor(state.minReps+(state.maxReps - state.minReps)/2)\n\n var.volume = 0\n var.target_volume = 0\n\n /// Analyze whether day was progress through planned sets. Note difference with progress above (hitting marks is not progress per se there)\n var.counter = 1\n for (var.set in completedWeights) {\n var.volume += completedWeights[var.counter] * completedReps[var.counter]\n if (var.counter <= programNumberOfSets) { // Calculate target volume in terms of program plan.\n var.target_volume += reps[var.counter] * weights[var.counter] // Note that this uses the rounded weights, not the \"originalWeights\", which would be an unfair penalty.\n }\n var.counter += 1\n }\n \n /// If didn't make at least one measure of progress do a dropset\n var.progress = var.target_volume*.999 < var.volume || state.maxvolume*.99 < var.volume \n\n if (!var.progress) {\n //add a set\n numberOfSets += 1\n /// sets(fromIndex, toIndex, minReps, maxReps, isAmrap, weight, timer, rpe, shouldLogRpe)\n var.intensity = 7 \n var.targetReps = floor(minReps[setIndex]+(reps[setIndex] - minReps[setIndex])/2)\n sets(numberOfSets, numberOfSets, minReps[setIndex], reps[setIndex], 0, var.e1RM * var.decay* rpeMultiplier(var.targetReps,var.intensity), 90, var.intensity, 1) \n }\n \n /// Update state.maxvolume here, so it excludes any dropsets. \"Core\" volume since don't want to be locked into extra sets as the only way to progress volume.\n state.maxvolume = state.maxvolume < var.volume ? var.volume : state.maxvolume\n\n }\n\n if (state.bodyweight > 0 && setIndex < programNumberOfSets) { \n print(weights[setIndex+1]-bodyweight)\n }\n\n /// Crude hack to implement deloading every time counter gets to 9x\n if (state.liftsincedeload >= 9) {\n weights = rm1 * .5\n timers = timers[1] *.6 // less rest\n }\n \n~} / progress: custom(minReps: 0, maxReps: 0, increment: 5lb, maxvolume: 0, maxweight: 0, liftsince1rm: 0, liftsincedeload: 0, liftsinceprogress: 0, bodyweight: 0) {~\n /// Target weight preserved internally via 1rm variable.\n\n // Max volume should have been checked in the last set update, but it is not accessible in current progress run.\n // Update other state variables. This requires recomputation of progress. \n\n state.maxweight = state.maxweight <= max(completedWeights) ? max(completedWeights) : state.maxweight\n state.liftsincedeload += 1\n state.liftsince1rm += 1\n state.liftsinceprogress +=1 // asssume failure modes, decrement otherwise.\n\n /// var.e1RM = calculate1RM(completedWeights[setIndex], completedReps[setIndex])\n for (var.set in reps) {\n var.RPE = completedRPE[var.set] > 0 ? completedRPE[var.set] : RPE[var.set] // Take entered RPE if available, if not, use the nominal target\n var.eRM1 = completedWeights[var.set] / rpeMultiplier(completedReps[var.set],var.RPE)\n var.delta = var.eRM1-rm1 \n if (var.delta / rm1 > .01) { // insist on a 1% change to avoid floating point problems in direct comparision.\n rm1 = var.eRM1\n state.liftsince1rm = 0\n state.liftsinceprogress = 0\n }\n } \n var.targetReps = floor(state.minReps+(state.maxReps - state.minReps)/2)\n \n var.volume = 0\n var.target_volume = 0\n\n /// Analyze whether day was progress through planned sets. Note difference with progress above (hitting marks is not progress per se there)\n var.counter = 1\n for (var.set in completedWeights) {\n var.volume += completedWeights[var.counter] * completedReps[var.counter]\n var.target_volume += var.targetReps * weights[var.counter] // Note that this uses the rounded weights, not the \"originalWeights\", which would be an unfair penalty.\n var.counter += 1\n }\n \n /// If didn't make at least one measure of progress do a dropset\n var.progress = var.target_volume*.999 < var.volume || state.maxvolume*.999 < var.volume \n\n /// No specific progression. Ratchet up as sets and weights increase. Remember that this will follow draining compound exercises.\n\n /// Note that if it = 9 above it would be > 9 here as it was incremented\n if (state.liftsincedeload > 9 || week == 3) {\n state.liftsincedeload = 0\n state.liftsince1rm -= 1\n state.liftsinceprogress -=1 // Un do previous increment\n } else {\n /// Generalized, very slow decay of 1RM. Only way it is changed. One could imagine it beingset from test sets or paying attention to RPE\n /// But this is more automated. Ratch up, decay down.\n if (!var.progress && state.liftsince1rm > 0) {\n rm1 = rm1 * .995\n }\n }\n~}"}],"description":"This \"Week\" is just to hold templates so that workouts on other weeks are cleaner."}]},"exercises":[],"vtype":"program","days":[],"deletedDays":[],"url":"","isMultiweek":true,"name":"2025 Automatic for the People V4","weeks":[],"nextDay":8,"updatedAt":1752685007085,"clonedAt":1763284863766,"description":"","tags":[],"deletedWeeks":[],"author":"","id":"xcmvxbvn"},"version":"20260304084247","settings":{"timers":{"warmup":90,"workout":210,"reminder":900},"units":"lb"}},"shouldSyncProgram":false,"isMobile":false,"revisions":[]}