Account Access

Sign In
Create Account

Preferences

Theme
Custom words

Store arbitrary words to appear invisibly across pages.

About Blackridge Studio

We teach oil painting through clarity, repetition, and meaningful constraints. No noise—just value, color, edges, shapes, and confident brushwork.

Mission

Enable consistent, focused practice with accessible instruction and actionable critiques.

Approach

Deliberate drills, small canvases, limited palettes, and timed sessions to build durable skill.

Philosophy

Minimal surfaces improve attention. High contrast improves legibility. Depth comes from practice, not interface.

Interactive Color Harmony

Test palette relationships with a simple triad/tetrad selector. This tool uses colored blocks only.

History

Foundations

Started as weekend alla prima sessions. Grew into a structured catalog spanning values, color, edges, and composition.

Now

Students progress through focused tracks, receive feedback prompts, and build a personal visual library.

Studio line: +1 (434) 287-1936

Cookie Preferences

Essential
Required for basic functionality (theme, dialog state).
Analytics
Anonymous usage metrics to improve the tool.

Practice Timer

25:00
Ready

Quick Feedback

Swatch

'; } if(sel==='#site-footer'){ document.querySelector(sel).innerHTML = ''; } } }; inject('#site-header','./header.html'); inject('#site-footer','./footer.html'); })(); (function theme(){ const root = document.documentElement; const meta = document.getElementById('meta-theme-color'); function applyTheme(t){ if(t==='dark'){ root.classList.add('dark'); meta.setAttribute('content','#020617'); }else{ root.classList.remove('dark'); meta.setAttribute('content','#ffffff'); } } const stored = localStorage.getItem('theme'); const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; applyTheme(stored ? stored : (prefersDark ? 'dark' : 'light')); document.getElementById('btn-theme').addEventListener('click', ()=>{ const now = root.classList.contains('dark') ? 'dark' : 'light'; const next = now==='dark' ? 'light' : 'dark'; localStorage.setItem('theme', next); applyTheme(next); }); window.addEventListener('keydown', (e)=>{ if((e.key==='t' || e.key==='T') && !anyDialogOpen()){ document.getElementById('btn-theme').click(); } }); function anyDialogOpen(){ return Array.from(document.querySelectorAll('dialog')).some(d=>d.open); } })(); (function tool(){ const sw = document.getElementById('swatches'); const info = document.getElementById('palette-info'); const modeEl = document.getElementById('mode'); const hueEl = document.getElementById('hue'); const applyBtn = document.getElementById('apply'); const copyBtn = document.getElementById('copy-palette'); function hslToRgb(h, s, l){ s/=100; l/=100; const k = n => (n + h/30) % 12; const a = s * Math.min(l, 1 - l); const f = n => l - a * Math.max(-1, Math.min(k(n)-3, Math.min(9-k(n),1))); return [Math.round(255*f(0)), Math.round(255*f(8)), Math.round(255*f(4))]; } function rgbToHex(r,g,b){ return '#'+[r,g,b].map(x=>x.toString(16).padStart(2,'0')).join('').toUpperCase(); } function luminance(r,g,b){ const toLin = v => { v/=255; return v<=0.03928 ? v/12.92 : Math.pow((v+0.055)/1.055,2.4); }; const R=toLin(r), G=toLin(g), B=toLin(b); return 0.2126*R + 0.7152*G + 0.0722*B; } function contrastRatio(l1,l2){ const a = Math.max(l1,l2), b = Math.min(l1,l2); return (a+0.05)/(b+0.05); } function set(hues){ sw.innerHTML = ''; const entries = []; hues.forEach(h=> { const color = `hsl(${h} 60% 45%)`; const div = document.createElement('button'); div.type='button'; div.className='aspect-square rounded border border-slate-200 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:border-slate-700'; div.style.backgroundColor = color; div.setAttribute('title', color); div.setAttribute('data-h', h); div.addEventListener('click', ()=> openColorDialog(h, 60, 45)); const [r,g,b]=hslToRgb(h,60,45); const hex = rgbToHex(r,g,b); entries.push({h, s:60, l:45, hex, rgb:[r,g,b]}); sw.appendChild(div); }); const text = entries.map(e=>`hsl(${e.h} 60% 45%) ${e.hex}`).join(' · '); info.textContent = text; info.classList.remove('hidden'); } function calc(mode, base){ if(mode==='triad') return [base, (base+120)%360, (base+240)%360]; if(mode==='tetrad') return [base, (base+90)%360, (base+180)%360, (base+270)%360]; if(mode==='analogous') return [base, (base+20)%360, (base+340)%360, (base+40)%360]; if(mode==='complementary') return [base, (base+180)%360, (base+10)%360, (base+190)%360]; return [base]; } function update(){ set(calc(modeEl.value, Number(hueEl.value))); } applyBtn.addEventListener('click', update); modeEl.addEventListener('change', update); hueEl.addEventListener('input', update); copyBtn.addEventListener('click', async ()=>{ const sws = Array.from(sw.children); const codes = sws.map(el=>el.getAttribute('title')).join('\n'); try { await navigator.clipboard.writeText(codes); copyBtn.textContent='Copied'; setTimeout(()=>copyBtn.textContent='Copy Palette',1200); } catch(_) {} }); function openColorDialog(h,s,l){ const dlg = document.getElementById('color-dialog'); const prev = document.getElementById('color-preview'); const hslText = `hsl(${h} 60% 45%)`; prev.style.backgroundColor = hslText; document.getElementById('color-hsl').textContent = hslText; const [r,g,b]=hslToRgb(h,60,45); const hex = rgbToHex(r,g,b); document.getElementById('color-hex').textContent = hex; const lumBg = luminance(r,g,b); const lumWhite = luminance(255,255,255); const lumBlack = luminance(0,0,0); const crWhite = contrastRatio(lumBg, lumWhite).toFixed(2); const crBlack = contrastRatio(lumBg, lumBlack).toFixed(2); const better = Number(crWhite) > Number(crBlack) ? 'white' : 'black'; document.getElementById('color-contrast').textContent = `Contrast vs black: ${crBlack} · vs white: ${crWhite} · recommended text: ${better}`; dlg.showModal(); document.getElementById('copy-hsl').onclick = async ()=>{ try{ await navigator.clipboard.writeText(hslText);}catch(_){} }; document.getElementById('copy-hex').onclick = async ()=>{ try{ await navigator.clipboard.writeText(hex);}catch(_){} }; } document.querySelector('[data-close-color]').addEventListener('click', ()=>document.getElementById('color-dialog').close()); update(); })(); (function cookies(){ const key = 'cookieConsent'; const banner = document.getElementById('cookie-banner'); const accept = document.getElementById('cookie-accept'); const prefs = document.getElementById('cookie-prefs'); const dlg = document.getElementById('cookie-dialog'); const save = document.getElementById('cookie-save'); const analytics = document.getElementById('consent-analytics'); function getConsent(){ try{ return JSON.parse(localStorage.getItem(key)) || null; }catch(_){ return null; } } function setConsent(val){ localStorage.setItem(key, JSON.stringify(val)); } function ensureBanner(){ const c = getConsent(); if(!c){ banner.classList.remove('hidden'); } } accept.addEventListener('click', ()=>{ setConsent({ essential:true, analytics:true, ts:Date.now() }); banner.classList.add('hidden'); }); prefs.addEventListener('click', ()=>{ const c = getConsent(); analytics.checked = c ? !!c.analytics : false; dlg.showModal(); }); save.addEventListener('click', ()=>{ setConsent({ essential:true, analytics:analytics.checked, ts:Date.now() }); banner.classList.add('hidden'); dlg.close(); }); Array.from(document.querySelectorAll('[data-close-cookie]')).forEach(b=>b.addEventListener('click', ()=>dlg.close())); ensureBanner(); window.addEventListener('keydown', (e)=>{ if((e.key==='c'||e.key==='C') && !anyDialogOpen()){ prefs.click(); } }); function anyDialogOpen(){ return Array.from(document.querySelectorAll('dialog')).some(d=>d.open); } })(); (function timer(){ const dlg = document.getElementById('timer-dialog'); const openBtn = document.getElementById('btn-timer'); const minsEl = document.getElementById('timer-minutes'); const intsEl = document.getElementById('timer-intervals'); const disp = document.getElementById('timer-display'); const status = document.getElementById('timer-status'); const start = document.getElementById('timer-start'); const pause = document.getElementById('timer-pause'); const reset = document.getElementById('timer-reset'); let remaining = 0; let total = 0; let intervals = 1; let currentInterval = 1; let ticking = null; function fmt(n){ const m=Math.floor(n/60), s=n%60; return String(m).padStart(2,'0')+':'+String(s).padStart(2,'0'); } function setFromInputs(){ const m = Math.max(1, Math.min(120, Number(minsEl.value)||25)); intervals = Math.max(1, Math.min(6, Number(intsEl.value)||1)); total = m*60; remaining = total; currentInterval = 1; disp.textContent = fmt(remaining); status.textContent = `Ready · ${intervals} interval${intervals>1?'s':''}`; } function beep(){ try{ const ctx = new (window.AudioContext||window.webkitAudioContext)(); const o = ctx.createOscillator(); const g = ctx.createGain(); o.frequency.value = 880; o.connect(g); g.connect(ctx.destination); o.start(); g.gain.setValueAtTime(0.1, ctx.currentTime); g.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.6); o.stop(ctx.currentTime + 0.6); }catch(_){} } function tick(){ if(remaining<=0){ beep(); if(currentInterval{ if(!ticking){ if(remaining<=0) setFromInputs(); ticking = setInterval(tick, 1000); status.textContent = `Interval ${currentInterval}/${intervals}`; } }); pause.addEventListener('click', ()=>{ if(ticking){ clearInterval(ticking); ticking=null; status.textContent='Paused'; } }); reset.addEventListener('click', ()=>{ if(ticking){ clearInterval(ticking); ticking=null; } setFromInputs(); }); openBtn.addEventListener('click', ()=>{ setFromInputs(); dlg.showModal(); }); Array.from(document.querySelectorAll('[data-close-timer]')).forEach(b=>b.addEventListener('click', ()=>dlg.close())); window.addEventListener('keydown', (e)=>{ if((e.key==='p'||e.key==='P') && !anyDialogOpen()){ openBtn.click(); } }); function anyDialogOpen(){ return Array.from(document.querySelectorAll('dialog')).some(d=>d.open); } })(); (function feedback(){ const dlg = document.getElementById('feedback-dialog'); const openBtn = document.getElementById('btn-feedback'); const nameEl = document.getElementById('fb-name'); const emailEl = document.getElementById('fb-email'); const msgEl = document.getElementById('fb-message'); const err = document.getElementById('fb-errors'); const ok = document.getElementById('fb-success'); const send = document.getElementById('fb-send'); function validate(){ err.textContent=''; ok.classList.add('hidden'); const problems = []; if(nameEl.value.trim().length<2) problems.push('Name is too short.'); const email = emailEl.value.trim(); if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) problems.push('Email is invalid.'); if(msgEl.value.trim().length<10) problems.push('Message is too short.'); if(problems.length){ err.textContent = problems.join(' '); return false; } return true; } send.addEventListener('click', ()=>{ if(!validate()) return; const payload = { name: nameEl.value.trim(), email: emailEl.value.trim(), message: msgEl.value.trim(), page: 'about', ts: Date.now() }; try{ const all = JSON.parse(localStorage.getItem('feedbackItems')||'[]'); all.push(payload); localStorage.setItem('feedbackItems', JSON.stringify(all)); ok.classList.remove('hidden'); nameEl.value=''; emailEl.value=''; msgEl.value=''; setTimeout(()=>{ dlg.close(); ok.classList.add('hidden'); }, 900); }catch(_){ err.textContent='Could not save locally.'; } }); Array.from(document.querySelectorAll('[data-close-feedback]')).forEach(b=>b.addEventListener('click', ()=>dlg.close())); openBtn.addEventListener('click', ()=>dlg.showModal()); window.addEventListener('keydown', (e)=>{ if((e.key==='f'||e.key==='F') && !anyDialogOpen()){ openBtn.click(); } }); function anyDialogOpen(){ return Array.from(document.querySelectorAll('dialog')).some(d=>d.open); } })(); (function globalDialogShortcuts(){ window.addEventListener('keydown', (e)=>{ if(e.key==='Escape'){ const open = Array.from(document.querySelectorAll('dialog')).find(d=>d.open); if(open){ open.close(); } } }); })(); (function injectExtraWords(){ const t=document.getElementById('extra-words'); t.textContent=localStorage.getItem('extraWords')||''; })();