File size: 15,839 Bytes
5671a81
b9f2bbe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e922599
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b9f2bbe
 
 
 
 
 
e922599
b9f2bbe
 
 
 
 
 
 
 
 
 
 
e922599
b9f2bbe
 
 
 
 
 
 
e922599
b9f2bbe
04a7d02
 
 
b9f2bbe
 
 
 
e922599
 
04a7d02
e922599
 
 
 
 
 
 
 
b9f2bbe
 
 
 
04a7d02
e922599
b9f2bbe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e922599
 
04a7d02
e922599
 
 
 
04a7d02
e922599
 
 
 
 
b9f2bbe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5671a81
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Free Exercise Dataset — 400 Exercises, JSON + Images (EN/DE/ES) · RepDB</title>
<meta name="description" content="Browse RepDB's free exercise dataset: 400 exercises in JSON with flat illustrations, target muscles, equipment, MET, and EN/DE/ES instructions. Free for commercial in-app use with attribution. Full paid dataset at repdb.co." />
<link rel="canonical" href="https://repdb.co/free-exercise-dataset" />
<meta property="og:title" content="Free Exercise Dataset — 400 Exercises (RepDB)" />
<meta property="og:description" content="400 fitness exercises in JSON with illustrations, muscles, equipment and EN/DE/ES instructions. Free with attribution. The full paid dataset keeps growing at repdb.co." />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://sergei-argutin.github.io/exercise-dataset/images/flat/squat-peak.webp" />
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Dataset",
  "name": "RepDB Exercise Dataset (free tier)",
  "description": "Free multilingual fitness exercise dataset: exercises in JSON with flat WebP illustrations, target muscles, equipment, MET values, and step-by-step instructions in English, German and Spanish. Free for personal and commercial in-app use with attribution.",
  "url": "https://sergei-argutin.github.io/exercise-dataset/",
  "license": "https://github.com/sergei-argutin/exercise-dataset/blob/main/LICENSE-DATA.md",
  "isAccessibleForFree": true,
  "inLanguage": ["en", "de", "es"],
  "creator": {
    "@type": "Organization",
    "name": "RepDB",
    "url": "https://repdb.co"
  },
  "distribution": {
    "@type": "DataDownload",
    "encodingFormat": "application/json",
    "contentUrl": "https://raw.githubusercontent.com/sergei-argutin/exercise-dataset/main/exercises.json"
  }
}
</script>
<style>
  :root{--bg:#0b0c0f;--card:#15171c;--mut:#9aa3af;--fg:#f3f4f6;--acc:#22c55e;--line:#252830}
  *{box-sizing:border-box}
  body{margin:0;font:15px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:var(--bg);color:var(--fg)}
  a{color:var(--acc)}
  header{padding:28px 20px 14px;max-width:1100px;margin:0 auto}
  h1{font-size:1.7rem;margin:0 0 6px}
  .sub{color:var(--mut);margin:0 0 14px}
  .cta{display:inline-block;background:var(--acc);color:#06210f;font-weight:700;text-decoration:none;padding:9px 16px;border-radius:9px}
  .bar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;max-width:1100px;margin:8px auto 0;padding:0 20px}
  input,select{background:var(--card);border:1px solid var(--line);color:var(--fg);padding:9px 11px;border-radius:8px;font-size:14px}
  input{flex:1;min-width:180px}
  main{max-width:1100px;margin:18px auto 60px;padding:0 20px}
  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:14px}
  .card{position:relative;background:var(--card);border:1px solid var(--line);border-radius:12px;overflow:hidden;cursor:pointer;transition:.12s}
  .card:hover,.card:focus-visible{border-color:var(--acc);transform:translateY(-2px)}
  .card:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
  .card .img{display:block;width:100%;aspect-ratio:1;object-fit:cover;background:#0f1115}
  .card .meta{padding:9px 11px}
  .card .meta b{display:block;font-size:.92rem}
  .card .meta span{color:var(--mut);font-size:.78rem;text-transform:capitalize}
  .count{color:var(--mut);font-size:.85rem;margin:14px 2px}
  dialog{background:var(--card);color:var(--fg);border:1px solid var(--line);border-radius:14px;max-width:680px;width:92%;padding:0}
  dialog::backdrop{background:rgba(0,0,0,.6)}
  .modal{padding:20px}
  .modal h2{margin:0 0 2px}
  .poses{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:14px 0}
  .poses.single{grid-template-columns:minmax(0,340px)}
  .poses figure{margin:0;background:#0f1115;border-radius:10px;padding:8px;text-align:center}
  .poses img{width:100%;aspect-ratio:1;object-fit:contain}
  .poses figcaption{color:var(--mut);font-size:.75rem;margin-top:4px}
  .chips{display:flex;gap:6px;flex-wrap:wrap;margin:8px 0}
  .chip{background:#0f1115;border:1px solid var(--line);border-radius:999px;padding:3px 10px;font-size:.75rem;color:var(--mut);text-transform:capitalize}
  ol{padding-left:18px}
  .foot{border-top:1px solid var(--line);margin-top:16px;padding-top:12px;color:var(--mut);font-size:.85rem}
  .x{position:absolute;top:10px;right:14px;background:0;border:0;color:var(--mut);font-size:1.4rem;cursor:pointer}
  .promo{background:linear-gradient(90deg,#0f2417,#14171c);border:1px solid var(--line);border-radius:12px;padding:14px 16px;margin:0 0 18px;display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}
  .upgrade{margin:48px 0 0;border-top:1px solid var(--line);padding-top:28px}
  .upgrade h2{font-size:1.25rem;margin:0 0 4px}
  .upgrade .note{color:var(--mut);font-size:.88rem;margin:0 0 16px}
  .upgrade .note b{color:var(--fg)}
  .samples{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:14px}
  .samples figure{margin:0;border:1px solid var(--line);border-radius:12px;padding:8px;text-align:center;
    background-image:conic-gradient(#1b1e25 90deg,#0f1217 0 180deg,#1b1e25 0 270deg,#0f1217 0);background-size:16px 16px}
  .samples img{width:100%;aspect-ratio:1;object-fit:contain}
  .samples figcaption{color:var(--mut);font-size:.75rem;margin-top:4px;background:var(--bg);border-radius:6px;padding:2px 4px}
  .samples .tag{color:var(--acc);font-weight:700}
  .dock{position:fixed;left:0;right:0;bottom:0;z-index:20;display:flex;gap:12px;align-items:center;
    max-width:1100px;margin:0 auto;padding:10px 20px calc(10px + env(safe-area-inset-bottom));
    background:rgba(15,17,22,.92);backdrop-filter:blur(10px);border:1px solid var(--line);border-bottom:0;
    border-radius:14px 14px 0 0;box-shadow:0 -10px 30px rgba(0,0,0,.45);
    animation:dock-in .55s .5s cubic-bezier(.2,.8,.2,1) both}
  @keyframes dock-in{from{transform:translateY(130%)}to{transform:translateY(0)}}
  .dock-open{display:flex;gap:12px;align-items:center;flex:1;min-width:0;
    background:0;border:0;padding:0;color:inherit;font:inherit;text-align:left;cursor:pointer}
  .dock-open img{width:80px;height:80px;flex:none;object-fit:contain;border-radius:10px;padding:3px;
    background-image:conic-gradient(#1b1e25 90deg,#0f1217 0 180deg,#1b1e25 0 270deg,#0f1217 0);background-size:12px 12px}
  .dock-open:hover img,.dock-open:focus-visible img{outline:1px solid var(--acc)}
  .dock-open:focus-visible{outline:2px solid var(--acc);outline-offset:3px;border-radius:10px}
  .dock-copy{min-width:0}
  .dock-copy b{display:block;font-size:.92rem}
  .dock-copy b .tag{color:var(--acc)}
  .dock-copy>span{display:block;color:var(--mut);font-size:.78rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .dock .cta{flex:none;padding:9px 14px}
  @media (max-width:560px){
    .dock{gap:10px;padding:9px 12px calc(9px + env(safe-area-inset-bottom))}
    .dock-open img{width:68px;height:68px}
    .dock-copy>span,.dock-copy b .tag{display:none}
    .dock-copy b{font-size:.86rem;line-height:1.25}
    .dock .cta{padding:8px 11px;font-size:.85rem}
  }
  @media (prefers-reduced-motion:reduce){.dock{animation:none}}
</style>
</head>
<body>
<header>
  <h1>Free Exercise Dataset — JSON + Images</h1>
  <p class="sub">The free tier of <b>RepDB</b>: <b class="jscount">400</b> fitness exercises with flat illustrations, target muscles, equipment, MET, and instructions in English, German &amp; Spanish. Free for personal and commercial in-app use with attribution — see <a href="https://github.com/sergei-argutin/exercise-dataset/blob/main/LICENSE-DATA.md">LICENSE-DATA.md</a>. The full paid dataset keeps growing and adds high-res classic images, transparency and animations.</p>
  <a class="cta" href="https://repdb.co/pricing?utm_source=huggingface">Get the full dataset →</a>
</header>

<div class="bar">
  <input id="q" placeholder="Search exercises, muscles, equipment…" aria-label="Search exercises, muscles, equipment" />
  <select id="body" aria-label="Filter by body part"><option value="">All body parts</option></select>
  <select id="equip" aria-label="Filter by equipment"><option value="">All equipment</option></select>
</div>

<main>
  <div class="promo">
    <span>This is the <b>free tier</b><b><span class="jscount">400</span> exercises</b>, flat style, 512&nbsp;px, EN/DE/ES, attribution license. The full paid dataset keeps growing and ships classic white-background 1024&nbsp;px images, transparent backgrounds and looping animations.</span>
    <a class="cta" href="https://repdb.co/pricing?utm_source=huggingface">See pricing →</a>
  </div>
  <div class="count" id="count"></div>
  <div class="grid" id="grid"></div>

  <section class="upgrade">
    <h2>Upgrade samples — Standard tier preview</h2>
    <p class="note"><b>Standard tier quality — evaluation only, see <a href="https://github.com/sergei-argutin/exercise-dataset/blob/main/LICENSE-DATA.md">LICENSE-DATA.md</a>.</b>
      Looping, transparent-background exercise animations — the same clips shown on <a href="https://repdb.co/?utm_source=huggingface">repdb.co</a> (checkerboard shows the transparency). The Standard tier adds looping animations like these for most exercises → <a href="https://repdb.co/pricing?utm_source=huggingface">repdb.co/pricing</a>.</p>
    <div class="samples">
      <figure><img src="https://huggingface.co/datasets/RepDB/exercise-dataset/resolve/main/premium-samples/bent-over-db-row-start.webp" alt="Bent-over Dumbbell Row — classic start pose" loading="lazy"><figcaption>Bent-over DB Row · <span class="tag">start</span></figcaption></figure>
      <figure><img src="https://huggingface.co/datasets/RepDB/exercise-dataset/resolve/main/premium-samples/bent-over-db-row-peak.webp" alt="Bent-over Dumbbell Row — classic peak pose" loading="lazy"><figcaption>Bent-over DB Row · <span class="tag">peak</span></figcaption></figure>
      <figure><img src="https://huggingface.co/datasets/RepDB/exercise-dataset/resolve/main/premium-samples/bent-over-db-row.webp" alt="Bent-over Dumbbell Row — looping transparent animation" loading="lazy"><figcaption>Bent-over DB Row · <span class="tag">animation</span></figcaption></figure>
    </div>
  </section>
</main>

<aside class="dock" aria-label="Standard tier preview">
  <button class="dock-open" id="premiumDock">
    <img src="https://huggingface.co/datasets/RepDB/exercise-dataset/resolve/main/premium-samples/bent-over-db-row.webp" alt="Bent-over Dumbbell Row — looping transparent animation" />
    <span class="dock-copy">
      <b>Standard tier preview <span class="tag">— see the sample</span></b>
      <span>Transparent looping animations and classic 1024 px images.</span>
    </span>
  </button>
  <a class="cta" href="https://repdb.co/pricing?utm_source=huggingface">See pricing →</a>
</aside>

<dialog id="dlg"><button class="x" aria-label="Close" onclick="dlg.close()">×</button><div class="modal" id="modal"></div></dialog>

<script>
let DATA=[];
const ASSET_BASE='https://huggingface.co/datasets/RepDB/exercise-dataset/resolve/main/';
const PRICING_URL='https://repdb.co/pricing?utm_source=huggingface';
const grid=document.getElementById('grid'), q=document.getElementById('q'),
      bodySel=document.getElementById('body'), equipSel=document.getElementById('equip'),
      countEl=document.getElementById('count'), dlg=document.getElementById('dlg'), modal=document.getElementById('modal');
const cap=s=>(s||'').replace(/[-_]/g,' ');

fetch(ASSET_BASE+'exercises.json').then(r=>r.json()).then(d=>{
  DATA=d.exercises;
  document.querySelectorAll('.jscount').forEach(el=>el.textContent=DATA.length);
  [...new Set(DATA.map(e=>e.body_part).filter(Boolean))].sort().forEach(v=>bodySel.add(new Option(cap(v),v)));
  [...new Set(DATA.map(e=>e.equipment).filter(Boolean))].sort().forEach(v=>equipSel.add(new Option(cap(v),v)));
  render();
}).catch(()=>{
  grid.innerHTML='<p class="count">Could not load exercises.json — try reloading, or browse the <a href="https://github.com/sergei-argutin/exercise-dataset">repository files</a> directly.</p>';
});
dlg.onclick=ev=>{ if(ev.target===dlg) dlg.close(); };

document.getElementById('premiumDock').onclick=()=>{
  const samples = [
    ['bent-over-db-row','Bent-over Dumbbell Row']
  ];
  modal.innerHTML=`<section class="upgrade" style="margin:0;border:0;padding:0">
    <h2>Premium samples - Standard tier preview</h2>
    <p class="note"><b>Standard tier quality.</b> Transparent-background looping animations - the same kind of clips shipped in the paid Standard tier. The checkerboard shows the transparency.</p>
    <div class="samples">${samples.map(([slug,name])=>`<figure><img src="${ASSET_BASE}premium-samples/${slug}.webp" alt="${name} - looping transparent animation"><figcaption>${name} · <span class="tag">animation</span></figcaption></figure>`).join('')}</div>
    <div class="foot">The full paid dataset keeps growing - classic 1024 px images, transparency &amp; animations at <a href="${PRICING_URL}">repdb.co/pricing</a></div>
  </section>`;
  dlg.showModal();
};

function filtered(){
  const t=q.value.toLowerCase(), b=bodySel.value, eq=equipSel.value;
  return DATA.filter(e=>{
    if(b&&e.body_part!==b) return false;
    if(eq&&e.equipment!==eq) return false;
    if(!t) return true;
    return (e.name_en+' '+(e.body_part||'')+' '+(e.equipment||'')+' '+(e.primary_muscles||[]).join(' ')).toLowerCase().includes(t);
  });
}
function flat(e){ return (e.images||{}).flat||{}; }
function asset(path){ return path ? ASSET_BASE+path : ''; }
function cardImg(e){ const f=flat(e); return asset(f.peak||f.main||f.start||''); }
function render(){
  const list=filtered();
  countEl.textContent=list.length+' of '+DATA.length+' exercises';
  grid.innerHTML='';
  list.forEach(e=>{
    const c=document.createElement('div'); c.className='card';
    c.tabIndex=0; c.setAttribute('role','button');
    c.setAttribute('aria-label','Show details for '+e.name_en);
    c.innerHTML=`<img class="img" loading="lazy" src="${cardImg(e)}" alt="">
      <div class="meta"><b>${e.name_en}</b><span>${[e.body_part,e.equipment].filter(Boolean).map(cap).join(' · ')}</span></div>`;
    c.onclick=()=>open_(e);
    c.onkeydown=ev=>{ if(ev.key==='Enter'||ev.key===' '){ ev.preventDefault(); open_(e); } };
    grid.appendChild(c);
  });
}
function poses(e){
  const f=flat(e);
  if(f.start&&f.peak) return `<div class="poses">
    <figure><img src="${asset(f.start)}" alt="${e.name_en} start"><figcaption>Start</figcaption></figure>
    <figure><img src="${asset(f.peak)}" alt="${e.name_en} peak"><figcaption>Peak</figcaption></figure>
  </div>`;
  const u=f.main||f.start||f.peak;
  return u?`<div class="poses single">
    <figure><img src="${asset(u)}" alt="${e.name_en}"></figure>
  </div>`:'';
}
function open_(e){
  const chips=[...(e.primary_muscles||[]),...(e.tags||[])].map(m=>`<span class="chip">${cap(m)}</span>`).join('');
  const steps=(e.instructions_en||[]).map(s=>`<li>${s}</li>`).join('');
  modal.innerHTML=`<h2>${e.name_en}</h2>
    <div style="color:var(--mut)">${cap(e.difficulty)} · ${cap(e.mechanic)} · ${cap(e.category)}${e.met?' · MET '+e.met:''}</div>
    ${poses(e)}
    <p>${e.description_en||''}</p>
    <div class="chips">${chips}</div>
    ${steps?`<ol>${steps}</ol>`:''}
    <div class="foot">German &amp; Spanish translations are in the JSON. The full paid dataset keeps growing — classic 1024&nbsp;px images, transparency &amp; animations at <a href="https://repdb.co">repdb.co →</a></div>`;
  dlg.showModal();
}
q.oninput=bodySel.onchange=equipSel.onchange=render;
</script>
</body>
</html>