{
  "schemaVersion": 1,
  "slug": "swimming-pace-calculator",
  "category": "sports",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "6a012241e4c573b78071ac4c7b5bdaa847e98a4341528bea3a5a83dcfeb33938",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/swimming-pace-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "P = 100(60m + s) ÷ d",
    "vars": [
      [
        "m",
        "Minutes swum"
      ],
      [
        "s",
        "Seconds"
      ],
      [
        "d",
        "Distance (m)"
      ],
      [
        "P",
        "Pace (s per 100 m)"
      ]
    ],
    "note": "Pace per 100 yd = P × 0.9144; speed = d ÷ (60m + s) m/s; pool lengths = ⌈d ÷ 25⌉."
  },
  "externalEvidence": {
    "reviewedAt": "2026-10-18",
    "sources": [
      {
        "name": "NIST SP 811 — SI conversion factors",
        "url": "https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors/nist-guide-si-appendix-b8"
      }
    ],
    "implementationHash": "6a012241e4c573b78071ac4c7b5bdaa847e98a4341528bea3a5a83dcfeb33938",
    "basis": "Divide elapsed seconds by distance in metres, then multiply by the selected pace distance: 100 m, 91.44 m for 100 yards, 457.2 m for 500 yards, or 1000 m for a kilometre. Round that total to the nearest second before formatting m:ss, so 119.6 seconds becomes 2:00, never 1:60. Speed is distance divided by elapsed seconds in m/s. The number of 25 m lengths is distance divided by 25 and rounded up; it is a pool-length budget, not a count of only completed lengths. Include stops in the entered time if you want an elapsed-time pace. This arithmetic does not predict ability, stroke efficiency or race performance.",
    "limitations": "Enter distance in metres and elapsed time in whole minutes and extra seconds. The pace selector changes only the displayed pace distance; it does not change the metre input. Pace is rounded to the nearest second. The pool-length budget always uses 25 m, including a partial final length rounded up."
  },
  "contract": {
    "compute": "const invalid={pace:null,totalMinutes:null,speedMps:null,lengths25m:null,__error:'inputError'};if(![distance,minutes,seconds].every(Number.isFinite)||distance<1||distance>30000||!Number.isInteger(minutes)||minutes<0||minutes>300||!Number.isInteger(seconds)||seconds<0||seconds>59||!['per100m','per100y','per500y','perKm'].includes(poolUnit))return invalid;\nconst totalSeconds=minutes*60+seconds;if(totalSeconds<=0)return invalid;const per100m=totalSeconds/distance*100;const fmt=s=>{if(!Number.isFinite(s)||s<0)return '—';const total=Math.round(s),m=Math.floor(total/60),sec=total%60;return m+':'+String(sec).padStart(2,'0');};const units={per100m,per100y:per100m*0.9144,per500y:per100m*0.9144*5,perKm:per100m*10};return{pace:fmt(units[poolUnit]),totalMinutes:totalSeconds/60,speedMps:distance/totalSeconds,lengths25m:Math.ceil(distance/25)};",
    "inputs": [
      {
        "name": "distance",
        "type": "number",
        "default": 1500,
        "min": 1,
        "max": 30000,
        "step": 25,
        "slider": true,
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "minutes",
        "type": "number",
        "default": 30,
        "min": 0,
        "max": 300,
        "step": 1,
        "slider": true,
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "seconds",
        "type": "number",
        "default": 0,
        "min": 0,
        "max": 59,
        "step": 1,
        "slider": true,
        "requiredValue": true,
        "strictRange": true
      },
      {
        "name": "poolUnit",
        "type": "select",
        "default": "per100m",
        "options": [
          {
            "value": "per100m"
          },
          {
            "value": "per100y"
          },
          {
            "value": "per500y"
          },
          {
            "value": "perKm"
          }
        ]
      }
    ],
    "outputs": [
      {
        "name": "pace",
        "precision": 0,
        "unit": "",
        "type": "text"
      },
      {
        "name": "totalMinutes",
        "precision": 2,
        "unit": "min"
      },
      {
        "name": "speedMps",
        "precision": 3,
        "unit": "m/s",
        "notation": "auto"
      },
      {
        "name": "lengths25m",
        "precision": 0,
        "unit": ""
      }
    ],
    "interpret": null
  }
}
