IJSEdge
— Methodology

How the
math works.

Every recommendation IJSEdge shows you is arithmetic, not opinion. Here's exactly how we compute it — formulas, thresholds, sample sizes, and the things we deliberately don't do.

01
Hidden Gains

The literal formula.

For every element a skater landed, we compute how it scored vs how the same element scored across the rest of the field in the same event. That delta, multiplied by the element's base value, is the number of points the skater either captured or left on the table for that one element.

deltai = (yours_GOEi − field_median_GOEi) × base_valuei
hidden_gain = Σ deltai

Field median is computed on every skater in the same event who attempted the same planned element. We use the median (not mean) because a single outlier judge or a single fall-and-recover skate would skew the average; the median absorbs both.

— Worked example
Kaitlyn Wright · Free Skate · The 38th Annual Florida State Games Figure Skating
ElementBaseYours GOEField median GOEΔ × base
Single Axel1.100.000.000.00
Double Loop1.700.00−0.27+0.46
Change Foot Comb. Spin1.600.00+0.16−0.26
Double Flip0.50−0.08−0.19+0.06
Step Sequence1.50−0.050.00−0.08
Double Salchow1.43−0.260.00−0.37
Fly. Change Foot Camel Spin1.50−0.300.00−0.45
Hidden gain potential = Σ Δ × base−0.64

Read this as: across these elements, the skater scored 0.64 points below the field median. That gap is the "hidden gain" — the point pool the next-level-up version of this same program is sitting on.

02
Element ROI

Six ways to add points.

For each element, the ROI engine considers six concrete things the skater could do and estimates the point gain range for each. Recommendations are ranked by gain and tagged with confidence + risk before they surface.

clean_up_call
Trigger: Element has been getting a UR / e / ! call in ≥ 2 of last 5 attempts.
Gain estimate: 0.5 – 2.5 pts/event at field-median GOE, depending on jump base value.
improve_goe
Trigger: Element lands clean but consistently scores below field-median GOE.
Gain estimate: 0.3 – 1.5 pts/event. Lowest risk of the six branches — the element is already in the program.
reduce_deductions
Trigger: Falls / time / costume / interruption deductions appearing in recent skates.
Gain estimate: 1.0 pt/fall + variable for non-fall deductions. Risk: low (these are unforced).
bonus
Trigger: An eligible jump pass is being attempted outside the late-program bonus window.
Gain estimate: 10% of base value when moved into the bonus window.
repeat_rule
Trigger: Skater is leaving a repeat-rule slot underused (e.g. only one triple repeated).
Gain estimate: Depends on which jump goes into the slot. Surfaces a candidate set.
upgrade
Trigger: Skater has shown a higher-value version of this element in practice / a previous level.
Gain estimate: Base-value delta. Risk: high — explicitly flagged as such on the recommendation.

Pure function, no opinion. The ROI engine reads the protocol breakdown and applies these rules deterministically — the same inputs always produce the same recommendations. Implementation: src/lib/analytics/element-roi.ts.

03
Move-Up Readiness

Signal, not advice.

The verdict is a composite of three measurable things across the skater's last several scored skates at the current level:

The composite drops the skater into one of five buckets:

This is a signal, not coaching advice. Every verdict ships with its evidence and explicit blockers, and the page consistently points the conversation back to the actual coach.

04
Clean Skate Index

0–100, with the reasons.

The Clean Skate Index summarizes how cleanly one program executed on a 0-to-100 scale. 100 is "no calls, no falls, every element credited" — a real Senior-level performance can run in the high 80s. The index drops for:

Every CSI score ships with the underlying reasons — you can always click through and see the per-element list of what was called and what each call cost. The parser handles both the prose form ("(under-rotated)") that USFS IJS protocols use and the bracket-suffix form ("2A<") that some secondary sources export.

Implementation: src/lib/analytics/clean-skate-index.ts.

05
Field percentiles

Within-event vs cross-event.

Within-event percentile ranks a skater against the field at the same competition + segment — exact peers, same panel, same date. This is the most defensible comparison and is what we lead with everywhere.

Cross-event percentile is broader: we compute the percentile at each event the skater competed in, then average across recent skates. It absorbs the "a weak field one week, a strong field the next" problem better than raw scores do.

Tie handling: when two skaters land on the same placement (rare for IJS, frequent for older protocols), both receive the higher percentile rank. Small fields: percentiles from fields under 6 skaters are flagged with a small-field warning so a 3-of-3 placement doesn't read as "33rd percentile" without context.

06
Per-judge variance

Panel spread, not judge bias.

Every IJS protocol ships nine judge columns per element. We import all of them and surface the spread (max − min across the panel) on each element + each component. A 4-point spread on one element means the panel disagreed; a flat 0-spread means they all saw the same skate.

We never call this "judge bias." The framing is panel spread — a measurement, not an accusation. Individual judges are not characterized; we are not competent to do so and the data does not support that read.

07
Confidence + sample size

Every recommendation is labelled.

Every Element ROI recommendation carries a confidence tag and a risk tag:

Recommendations with high-confidence + low-risk surface at the top. Speculative ones (low-confidence + high-risk) are visible but explicitly labelled — so the reader can decide for themselves how much weight to give each.

08
What we don't do

The deliberate omissions.

IJSEdge does not:

09
Data sources + update cadence

Where the numbers come from.

Primary source: official USFS IJS leaderboards (the CAT…SEG…html pages generated by ijsLive during a competition). Every element, GOE, component score, and deduction comes from there — we don't synthesize.

Secondary sources: for competitions not published on the USFS IJS feed (smaller events, international, summer comps), we ingest the flat structure (placements, totals) from public secondary sources and parse element breakdowns where available.

Rate limits are conservative across every upstream fetcher. All imports are idempotent on a deterministic (competition, event, skater) hash so re-running an import doesn't duplicate.

Supersession rule: when multiple sources have the same competition, the official IJS row always wins. Secondary sources fill gaps, never overwrite authoritative data.

Cadence: the homepage poller runs every few minutes during competition weekends. New competitions appear within minutes of the result page being published. Off-season cadence drops to daily.

10
Limitations

What we don't know yet.

— Reader questions

A few we hear
often.

Is this AI?

No. Everything on the analytics surface is arithmetic over the protocol — sums, medians, deltas, percentiles. No model artifacts. If we ever ship an AI-generated surface, it will be clearly labelled as such and stay off the analytical primitives.

Can I see the source code for a specific formula?

The analytics layer lives in src/lib/analytics/. Hidden Gains is a sum; Element ROI is the six-branch decision tree linked above; Clean Skate Index is a per-element penalty model; percentiles are textbook rank-stats with explicit tie + small-field handling.

What about international competitions?

US-only at launch — sectionals, regionals, championships, summer comps. International events (Skate America, Grand Prix, Worlds) are on the roadmap pending data-source agreements with the ISU feeds.
About the author → Built by Josh — skating dad, AHCI.