HARMONY RIGS
(() => { const FONT_FAMILY = 'JonFont, Helvetica, Arial, sans-serif'; function renderTitle(wrapper) { const source = wrapper.querySelector('.jonfont-title-text'); if (!source) return; const title = source.textContent.trim(); wrapper.querySelector('svg')?.remove(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); text.setAttribute('font-family', FONT_FAMILY); text.setAttribute('font-size', '300'); text.setAttribute('fill', '#35358c'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = title; svg.appendChild(text); wrapper.appendChild(svg); const fit = () => { try { const box = text.getBBox(); const padX = Math.max(64, box.width * 0.035); const padY = 18; svg.setAttribute( 'viewBox', `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` ); text.setAttribute('x', box.x + box.width / 2); text.setAttribute('y', box.y + box.height / 2 + box.height * 0.03); } catch {} }; fit(); window.addEventListener('resize', fit); } const init = () => { document .querySelectorAll('.jonfont-title') .forEach(renderTitle); }; if (document.fonts?.ready) { document.fonts.ready.then(init); } else { window.addEventListener('load', init); } })();
(() => { const REPO = 'https://api.github.com/repos/J0onbo0n/Harmony-Rigs-Images/contents'; const META = [ { title: 'Richard Rig', link: '#richard-rig' }, { title: 'Cat Rig', link: '#cat-rig' }, { title: 'Milly Rig', link: '#milly-rig' }, { title: 'Bert and Doug Rigs', link: '#bert-doug-rigs' } ]; const gallery = document.getElementById('hr-gallery'); if (!gallery) return; fetch(REPO) .then(r => r.json()) .then(files => { files .filter(f => /\.webp$/i.test(f.name)) .sort((a, b) => { const na = parseInt(a.name, 10); const nb = parseInt(b.name, 10); return na - nb; }) .slice(0, META.length) .forEach((file, i) => { const a = document.createElement('a'); a.className = 'hr-item'; a.href = META[i].link; const img = document.createElement('img'); img.src = file.download_url; img.loading = 'lazy'; img.alt = META[i].title; const cap = document.createElement('div'); cap.className = 'hr-caption'; cap.textContent = META[i].title; a.append(img, cap); gallery.appendChild(a); }); }) .catch(() => { /* fail silently so Carrd never blanks */ }); })();
(() => { document.addEventListener("click", e => { const btn = e.target.closest(".back-to-top-btn"); if (!btn) return; e.preventDefault(); window.scrollTo({ top: 0, behavior: "smooth" }); }); })();
RICHARD RIG
(() => { const FONT_FAMILY = 'JonFont, Helvetica, Arial, sans-serif'; function renderTitle(wrapper) { const source = wrapper.querySelector('.jonfont-title-text'); if (!source) return; const title = source.textContent.trim(); wrapper.querySelector('svg')?.remove(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); text.setAttribute('font-family', FONT_FAMILY); text.setAttribute('font-size', '300'); text.setAttribute('fill', '#35358c'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = title; svg.appendChild(text); wrapper.appendChild(svg); const fit = () => { try { const box = text.getBBox(); const padX = Math.max(64, box.width * 0.035); const padY = 18; svg.setAttribute( 'viewBox', `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` ); text.setAttribute('x', box.x + box.width / 2); text.setAttribute('y', box.y + box.height / 2 + box.height * 0.03); } catch {} }; fit(); window.addEventListener('resize', fit); } const init = () => { document .querySelectorAll('.jonfont-title') .forEach(renderTitle); }; if (document.fonts?.ready) { document.fonts.ready.then(init); } else { window.addEventListener('load', init); } })();
CAT RIG
(() => { const FONT_FAMILY = 'JonFont, Helvetica, Arial, sans-serif'; function renderTitle(wrapper) { const source = wrapper.querySelector('.jonfont-title-text'); if (!source) return; const title = source.textContent.trim(); wrapper.querySelector('svg')?.remove(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); text.setAttribute('font-family', FONT_FAMILY); text.setAttribute('font-size', '300'); text.setAttribute('fill', '#35358c'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = title; svg.appendChild(text); wrapper.appendChild(svg); const fit = () => { try { const box = text.getBBox(); const padX = Math.max(64, box.width * 0.035); const padY = 18; svg.setAttribute( 'viewBox', `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` ); text.setAttribute('x', box.x + box.width / 2); text.setAttribute('y', box.y + box.height / 2 + box.height * 0.03); } catch {} }; fit(); window.addEventListener('resize', fit); } const init = () => { document .querySelectorAll('.jonfont-title') .forEach(renderTitle); }; if (document.fonts?.ready) { document.fonts.ready.then(init); } else { window.addEventListener('load', init); } })();
MILLY RIG
(() => { const FONT_FAMILY = 'JonFont, Helvetica, Arial, sans-serif'; function renderTitle(wrapper) { const source = wrapper.querySelector('.jonfont-title-text'); if (!source) return; const title = source.textContent.trim(); wrapper.querySelector('svg')?.remove(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); text.setAttribute('font-family', FONT_FAMILY); text.setAttribute('font-size', '300'); text.setAttribute('fill', '#35358c'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = title; svg.appendChild(text); wrapper.appendChild(svg); const fit = () => { try { const box = text.getBBox(); const padX = Math.max(64, box.width * 0.035); const padY = 18; svg.setAttribute( 'viewBox', `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` ); text.setAttribute('x', box.x + box.width / 2); text.setAttribute('y', box.y + box.height / 2 + box.height * 0.03); } catch {} }; fit(); window.addEventListener('resize', fit); } const init = () => { document .querySelectorAll('.jonfont-title') .forEach(renderTitle); }; if (document.fonts?.ready) { document.fonts.ready.then(init); } else { window.addEventListener('load', init); } })();
BERT AND DOUG RIGS
(() => { const FONT_FAMILY = 'JonFont, Helvetica, Arial, sans-serif'; function renderTitle(wrapper) { const source = wrapper.querySelector('.jonfont-title-text'); if (!source) return; const title = source.textContent.trim(); wrapper.querySelector('svg')?.remove(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); text.setAttribute('font-family', FONT_FAMILY); text.setAttribute('font-size', '300'); text.setAttribute('fill', '#35358c'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = title; svg.appendChild(text); wrapper.appendChild(svg); const fit = () => { try { const box = text.getBBox(); const padX = Math.max(64, box.width * 0.035); const padY = 18; svg.setAttribute( 'viewBox', `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` ); text.setAttribute('x', box.x + box.width / 2); text.setAttribute('y', box.y + box.height / 2 + box.height * 0.03); } catch {} }; fit(); window.addEventListener('resize', fit); } const init = () => { document .querySelectorAll('.jonfont-title') .forEach(renderTitle); }; if (document.fonts?.ready) { document.fonts.ready.then(init); } else { window.addEventListener('load', init); } })();
DEMO REEL
(() => { const FONT_FAMILY = 'JonFont, Helvetica, Arial, sans-serif'; function renderTitle(wrapper) { const source = wrapper.querySelector('.jonfont-title-text'); if (!source) return; const title = source.textContent.trim(); wrapper.querySelector('svg')?.remove(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); text.setAttribute('font-family', FONT_FAMILY); text.setAttribute('font-size', '300'); text.setAttribute('fill', '#35358c'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = title; svg.appendChild(text); wrapper.appendChild(svg); const fit = () => { try { const box = text.getBBox(); const padX = Math.max(64, box.width * 0.035); const padY = 18; svg.setAttribute( 'viewBox', `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` ); text.setAttribute('x', box.x + box.width / 2); text.setAttribute('y', box.y + box.height / 2 + box.height * 0.03); } catch {} }; fit(); window.addEventListener('resize', fit); } const init = () => { document .querySelectorAll('.jonfont-title') .forEach(renderTitle); }; if (document.fonts?.ready) { document.fonts.ready.then(init); } else { window.addEventListener('load', init); } })();
Video
FIGURE DRAWING
(() => { const FONT_FAMILY = 'JonFont, Helvetica, Arial, sans-serif'; function renderTitle(wrapper) { const source = wrapper.querySelector('.jonfont-title-text'); if (!source) return; const title = source.textContent.trim(); wrapper.querySelector('svg')?.remove(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); text.setAttribute('font-family', FONT_FAMILY); text.setAttribute('font-size', '300'); text.setAttribute('fill', '#35358c'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = title; svg.appendChild(text); wrapper.appendChild(svg); const fit = () => { try { const box = text.getBBox(); const padX = Math.max(64, box.width * 0.035); const padY = 18; svg.setAttribute( 'viewBox', `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` ); text.setAttribute('x', box.x + box.width / 2); text.setAttribute('y', box.y + box.height / 2 + box.height * 0.03); } catch {} }; fit(); window.addEventListener('resize', fit); } const init = () => { document .querySelectorAll('.jonfont-title') .forEach(renderTitle); }; if (document.fonts?.ready) { document.fonts.ready.then(init); } else { window.addEventListener('load', init); } })();
(() => { const REPO = 'https://api.github.com/repos/J0onbo0n/FigureDrawing-Images/contents'; const gallery = document.getElementById('gallery'); let rowHeight, gap; function measure() { const cs = getComputedStyle(gallery); rowHeight = parseInt(cs.gridAutoRows); gap = parseInt(cs.rowGap); } function spanFor(h) { return Math.ceil((h + gap) / (rowHeight + gap)); } function layoutItem(item) { const el = item.classList.contains('loaded') ? item.querySelector('img') : item.querySelector('.jb-ph'); const h = el.getBoundingClientRect().height; if (h) item.style.gridRowEnd = `span ${spanFor(h)}`; } fetch(REPO) .then(r => r.json()) .then(files => { files .filter(f => /\.webp$/i.test(f.name)) .sort((a,b)=>a.name.localeCompare(b.name,undefined,{numeric:true})) .forEach(file => { const item = document.createElement('div'); item.className = 'jb-item'; const ph = document.createElement('div'); ph.className = 'jb-ph'; const img = document.createElement('img'); img.src = file.download_url; item.append(ph, img); gallery.appendChild(item); measure(); layoutItem(item); img.onload = () => { const ar = img.naturalWidth / img.naturalHeight; ph.style.aspectRatio = ar; item.style.setProperty('--ar', ar); if (ar > 16/9) item.classList.add('wide'); measure(); layoutItem(item); requestAnimationFrame(() => { item.classList.add('loaded'); layoutItem(item); }); }; new ResizeObserver(() => layoutItem(item)).observe(item); }); }); /* LIGHTBOX — unchanged */ const lightbox = document.getElementById('lightbox'); const img = document.getElementById('lightbox-img'); const layer = document.getElementById('layer'); const wrap = document.getElementById('wrap'); const closeBtn = document.getElementById('close'); const nextBtn = document.getElementById('next'); const prevBtn = document.getElementById('prev'); let index = 0, zoom = 1, base = 1, panX = 0, panY = 0; let dragging = false, sx = 0, sy = 0; const thumbs = () => [...document.querySelectorAll('.jb-item img')]; const isMobile = () => matchMedia('(max-width: 768px)').matches; function apply() { layer.style.transform = `translate3d(${panX}px, ${panY}px, 0) scale(${zoom * base})`; } function fit() { if (!wrap.clientWidth || !img.naturalWidth) return; const cs = getComputedStyle(document.documentElement); const pad = parseInt(cs.getPropertyValue('--safe-pad')); const gx = parseInt(cs.getPropertyValue('--ui-gutter-x')); const gy = parseInt(cs.getPropertyValue('--ui-gutter-top')); const fw = wrap.clientWidth - gx * 2 - pad * 2; const fh = wrap.clientHeight - gy - pad * 2; base = Math.min(fw / img.naturalWidth, fh / img.naturalHeight) * 1.15; panX = (wrap.clientWidth - img.naturalWidth * base) / 2; panY = (wrap.clientHeight - img.naturalHeight * base) / 2 + gy * 0.25; zoom = 1; apply(); } function openAt(i) { const t = thumbs(); if (!t[i]) return; index = i; img.src = t[i].src; lightbox.classList.add('active'); document.body.style.overflow = 'hidden'; img.onload = fit; requestAnimationFrame(fit); } function close() { lightbox.classList.remove('active'); document.body.style.overflow = ''; } document.addEventListener('pointerup', e => { if (isMobile()) return; const el = e.target.closest('.jb-item img'); if (!el) return; const i = thumbs().indexOf(el); if (i !== -1) openAt(i); }, true); closeBtn.onclick = close; nextBtn.onclick = () => openAt((index + 1) % thumbs().length); prevBtn.onclick = () => openAt((index - 1 + thumbs().length) % thumbs().length); wrap.addEventListener('wheel', e => { e.preventDefault(); const r = img.getBoundingClientRect(); const ox = (e.clientX - r.left) / (zoom * base); const oy = (e.clientY - r.top) / (zoom * base); const p = zoom; zoom = Math.min(Math.max(1, zoom + e.deltaY * -0.001), 4.5); if (zoom === 1) return fit(); panX -= ox * (zoom - p) * base; panY -= oy * (zoom - p) * base; apply(); },{passive:false}); img.addEventListener('pointerdown', e => { if (zoom <= 1) return; dragging = true; sx = e.clientX - panX; sy = e.clientY - panY; img.classList.add('dragging'); }); window.addEventListener('pointermove', e => { if (!dragging) return; panX = e.clientX - sx; panY = e.clientY - sy; apply(); }); window.addEventListener('pointerup', () => { dragging = false; img.classList.remove('dragging'); }); document.addEventListener('keydown', e => { if (e.key === 'Escape') close(); }); })();
(() => { document.addEventListener("click", e => { const btn = e.target.closest(".back-to-top-btn"); if (!btn) return; e.preventDefault(); window.scrollTo({ top: 0, behavior: "smooth" }); }); })();
ABOUT ME
(() => { const FONT_FAMILY = 'JonFont, Helvetica, Arial, sans-serif'; function renderTitle(wrapper) { const source = wrapper.querySelector('.jonfont-title-text'); if (!source) return; const title = source.textContent.trim(); wrapper.querySelector('svg')?.remove(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); text.setAttribute('font-family', FONT_FAMILY); text.setAttribute('font-size', '300'); text.setAttribute('fill', '#35358c'); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = title; svg.appendChild(text); wrapper.appendChild(svg); const fit = () => { try { const box = text.getBBox(); const padX = Math.max(64, box.width * 0.035); const padY = 18; svg.setAttribute( 'viewBox', `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` ); text.setAttribute('x', box.x + box.width / 2); text.setAttribute('y', box.y + box.height / 2 + box.height * 0.03); } catch {} }; fit(); window.addEventListener('resize', fit); } const init = () => { document .querySelectorAll('.jonfont-title') .forEach(renderTitle); }; if (document.fonts?.ready) { document.fonts.ready.then(init); } else { window.addEventListener('load', init); } })();
Hello! My name is Jonathan Bennett, I am a 2D Rigging/Builds Artist and Animator using ToonBoom Harmony to bring characters to life. I have always had a passion for art and problem solving, and am always striving to learn and share my knowledge with others. Let's help each other grow!

To get in touch with me, reach out to [email protected]