HIGH QUALITY // LOW PRICE
Custom-built websites that look like they came from a big agency, at a fraction of the price. One designer, one fixed price, no surprises.
That’s 8.51% of the average price.
Most of a website’s price pays for a big team. Omega is one person who builds every site personally, so the price stays low and the quality doesn’t follow it down.
Get in touch and tell me about your business, your customers and what you want the site to do.
Your site is built from scratch, by hand, around your business. No templates.
You check it over, we make any tweaks, and your new website goes live.
I remember sitting in class one day, realising that path wasn’t for me. Programming is my passion, and I thank my clients for making it my living.
There’s no big team, no account managers and no agency markup. Omega is one person, so you only pay for the work itself.
[Explain exactly what’s included, and anything charged separately such as domain or hosting.]
[Your typical turnaround time.]
Yes. Every site is built to work on phones, tablets and desktops.
Just get in touch and tell me about your business. We’ll take it from there.
Tell me about your business and I’ll get back to you.
omegawebsites@outlook.com
<script> // Nav scroll state const nav = document.getElementById('navbar'); window.addEventListener('scroll', () => { nav.classList.toggle('scrolled', window.scrollY > 20); }); // Scroll reveal const revealEls = document.querySelectorAll('.reveal'); const io = new IntersectionObserver((entries) => { entries.forEach(e => { if(e.isIntersecting){ e.target.classList.add('in'); io.unobserve(e.target); } }); }, {threshold:0.15}); revealEls.forEach(el => io.observe(el)); // Hero mesh — node lattice that drifts and connects, brightens near cursor (function(){ const canvas = document.getElementById('mesh'); const ctx = canvas.getContext('2d'); let w, h, nodes = []; const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; function resize(){ w = canvas.width = canvas.offsetWidth * devicePixelRatio; h = canvas.height = canvas.offsetHeight * devicePixelRatio; } function init(){ resize(); const count = Math.floor((w*h) / (26000 * devicePixelRatio*devicePixelRatio)); nodes = Array.from({length: Math.min(count, 90)}, () => ({ x: Math.random()*w, y: Math.random()*h, vx: (Math.random()-0.5)*0.15, vy: (Math.random()-0.5)*0.15, r: Math.random()*1.5+0.5 })); } let mouse = {x:-9999,y:-9999}; canvas.addEventListener('mousemove', e => { const rect = canvas.getBoundingClientRect(); mouse.x = (e.clientX-rect.left)*devicePixelRatio; mouse.y = (e.clientY-rect.top)*devicePixelRatio; }); canvas.addEventListener('mouseleave', () => { mouse.x=-9999; mouse.y=-9999; }); function draw(){ ctx.clearRect(0,0,w,h); const maxDist = 150*devicePixelRatio; for(let i=0;i<nodes.length;i++){ const n = nodes[i]; if(!reduceMotion){ n.x += n.vx; n.y += n.vy; if(n.x<0||n.x>w) n.vx*=-1; if(n.y<0||n.y>h) n.vy*=-1; } for(let j=i+1;j<nodes.length;j++){ const m = nodes[j]; const dx=n.x-m.x, dy=n.y-m.y; const dist=Math.sqrt(dx*dx+dy*dy); if(dist<maxDist){ const mdx=(n.x+m.x)/2-mouse.x, mdy=(n.y+m.y)/2-mouse.y; const mdist = Math.sqrt(mdx*mdx+mdy*mdy); const glow = Math.max(0, 1-mdist/(260*devicePixelRatio)); const alpha = (1-dist/maxDist)*0.14 + glow*0.35; ctx.strokeStyle = glow>0.15 ? `rgba(255,176,32,${alpha})` : `rgba(255,90,31,${alpha})`; ctx.lineWidth = 1*devicePixelRatio; ctx.beginPath(); ctx.moveTo(n.x,n.y); ctx.lineTo(m.x,m.y); ctx.stroke(); } } } for(const n of nodes){ const mdx=n.x-mouse.x, mdy=n.y-mouse.y; const mdist = Math.sqrt(mdx*mdx+mdy*mdy); const glow = Math.max(0, 1-mdist/(220*devicePixelRatio)); ctx.beginPath(); ctx.arc(n.x,n.y,n.r*devicePixelRatio + glow*1.5, 0, Math.PI*2); ctx.fillStyle = glow>0.1 ? `rgba(255,176,32,${0.5+glow*0.5})` : 'rgba(168,150,134,0.4)'; ctx.fill(); } requestAnimationFrame(draw); } window.addEventListener('resize', init); init(); draw(); })(); // Architecture panel — rotating shard graph (function(){ const canvas = document.getElementById('archCanvas'); const ctx = canvas.getContext('2d'); let w,h; const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; function resize(){ w = canvas.width = canvas.offsetWidth*devicePixelRatio; h = canvas.height = canvas.offsetHeight*devicePixelRatio; } const ro = new ResizeObserver(resize); ro.observe(canvas); resize(); const rings = [0.32, 0.58, 0.85]; const nodesPerRing = [6, 10, 14]; let t = 0; function draw(){ ctx.clearRect(0,0,w,h); const cx=w/2, cy=h/2; const baseR = Math.min(w,h)/2; rings.forEach((rf, ri) => { const r = baseR*rf; ctx.strokeStyle = 'rgba(28,32,48,0.9)'; ctx.lineWidth = 1*devicePixelRatio; ctx.beginPath(); ctx.arc(cx,cy,r,0,Math.PI*2); ctx.stroke(); const count = nodesPerRing[ri]; const speed = 0.0004 * (ri%2===0?1:-1); for(let i=0;i<count;i++){ const angle = (i/count)*Math.PI*2 + t*speed*(ri+1); const x = cx + Math.cos(angle)*r; const y = cy + Math.sin(angle)*r; const pulse = (Math.sin(t*0.002 + i + ri*3)+1)/2; ctx.beginPath(); ctx.arc(x,y, (1.6+pulse*1.8)*devicePixelRatio, 0, Math.PI*2); ctx.fillStyle = ri===1 && pulse>0.7 ? '#ffb020' : 'rgba(255,90,31,'+(0.4+pulse*0.5)+')'; ctx.fill(); if(i%3===0){ const angle2 = ((i+1)/count)*Math.PI*2 + t*speed*(ri+1); const x2 = cx + Math.cos(angle2)*r; const y2 = cy + Math.sin(angle2)*r; ctx.strokeStyle = 'rgba(255,90,31,0.18)'; ctx.lineWidth = 1*devicePixelRatio; ctx.beginPath(); ctx.moveTo(x,y); ctx.lineTo(x2,y2); ctx.stroke(); } } }); ctx.beginPath(); ctx.arc(cx,cy, 4*devicePixelRatio, 0, Math.PI*2); ctx.fillStyle = '#e7e9f0'; ctx.fill(); ctx.beginPath(); ctx.arc(cx,cy, 10*devicePixelRatio, 0, Math.PI*2); ctx.strokeStyle = 'rgba(231,233,240,0.25)'; ctx.lineWidth = 1*devicePixelRatio; ctx.stroke(); if(!reduceMotion) t += 16; requestAnimationFrame(draw); } draw(); })(); // Process — a circuit-board grid that brightens near the cursor, with // signal pulses traveling along the lines. Echoes "process" as literal current. function initCircuitGrid(canvasId){ const canvas = document.getElementById(canvasId); if(!canvas) return; const section = canvas.parentElement; const ctx = canvas.getContext('2d'); let w, h; const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const spacingCss = 46; let pulses = []; function resize(){ w = canvas.width = canvas.offsetWidth * devicePixelRatio; h = canvas.height = canvas.offsetHeight * devicePixelRatio; } function spawnPulse(){ const horizontal = Math.random() < 0.5; const spacing = spacingCss * devicePixelRatio; const lines = Math.floor((horizontal ? h : w) / spacing); pulses.push({ horizontal, line: Math.floor(Math.random()*lines) * spacing, pos: -30*devicePixelRatio, speed: (0.5 + Math.random()*0.7) * devicePixelRatio, len: (60 + Math.random()*50) * devicePixelRatio }); } let mouse = {x:-9999, y:-9999}; section.addEventListener('mousemove', e => { const rect = canvas.getBoundingClientRect(); mouse.x = (e.clientX-rect.left) * devicePixelRatio; mouse.y = (e.clientY-rect.top) * devicePixelRatio; }); section.addEventListener('mouseleave', () => { mouse.x=-9999; mouse.y=-9999; }); function draw(){ ctx.clearRect(0,0,w,h); const spacing = spacingCss * devicePixelRatio; const glowR = 170 * devicePixelRatio; ctx.lineWidth = 1*devicePixelRatio; for(let x=0; x<=w; x+=spacing){ const glow = Math.max(0, 1-Math.abs(x-mouse.x)/glowR); ctx.strokeStyle = `rgba(255,90,31,${0.05+glow*0.22})`; ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,h); ctx.stroke(); } for(let y=0; y<=h; y+=spacing){ const glow = Math.max(0, 1-Math.abs(y-mouse.y)/glowR); ctx.strokeStyle = `rgba(255,90,31,${0.05+glow*0.22})`; ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(w,y); ctx.stroke(); } for(let x=0; x<=w; x+=spacing){ for(let y=0; y<=h; y+=spacing){ const dist = Math.hypot(x-mouse.x, y-mouse.y); const glow = Math.max(0, 1-dist/(140*devicePixelRatio)); if(glow > 0.03){ ctx.beginPath(); ctx.arc(x,y,(1+glow*2)*devicePixelRatio,0,Math.PI*2); ctx.fillStyle = `rgba(255,176,32,${glow*0.85})`; ctx.fill(); } } } if(!reduceMotion){ if(Math.random()<0.025 && pulses.length<14) spawnPulse(); pulses.forEach(p => p.pos += p.speed); pulses = pulses.filter(p => p.pos < (p.horizontal?w:h) + 60*devicePixelRatio); } pulses.forEach(p => { const x1 = p.horizontal ? p.pos : p.line; const y1 = p.horizontal ? p.line : p.pos; const x0 = p.horizontal ? p.pos-p.len : p.line; const y0 = p.horizontal ? p.line : p.pos-p.len; const grad = ctx.createLinearGradient(x0,y0,x1,y1); grad.addColorStop(0,'rgba(255,176,32,0)'); grad.addColorStop(1,'rgba(255,176,32,0.85)'); ctx.strokeStyle = grad; ctx.lineWidth = 2*devicePixelRatio; ctx.beginPath(); ctx.moveTo(x0,y0); ctx.lineTo(x1,y1); ctx.stroke(); }); requestAnimationFrame(draw); } window.addEventListener('resize', resize); resize(); draw(); } // Logos — a quiet dotted grid with a slow light sweep passing through, // like a scanner confirming each name in turn. function initScanSweep(canvasId){ const canvas = document.getElementById(canvasId); if(!canvas) return; const ctx = canvas.getContext('2d'); let w, h, t = 0; const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; function resize(){ w = canvas.width = canvas.offsetWidth * devicePixelRatio; h = canvas.height = canvas.offsetHeight * devicePixelRatio; } function draw(){ ctx.clearRect(0,0,w,h); const spacing = 28*devicePixelRatio; ctx.fillStyle = 'rgba(168,150,134,0.18)'; for(let x=spacing/2; x<w; x+=spacing){ for(let y=spacing/2; y<h; y+=spacing){ ctx.beginPath(); ctx.arc(x,y,1*devicePixelRatio,0,Math.PI*2); ctx.fill(); } } const sweepW = 220*devicePixelRatio; const cycle = w + sweepW*2; const x = (t % cycle) - sweepW; const grad = ctx.createLinearGradient(x-sweepW/2,0,x+sweepW/2,0); grad.addColorStop(0,'rgba(255,90,31,0)'); grad.addColorStop(0.5,'rgba(255,176,32,0.16)'); grad.addColorStop(1,'rgba(255,90,31,0)'); ctx.fillStyle = grad; ctx.fillRect(x-sweepW/2,0,sweepW,h); if(!reduceMotion) t += 2*devicePixelRatio; requestAnimationFrame(draw); } window.addEventListener('resize', resize); resize(); draw(); } // Quote — a soft ambient glow that drifts on its own and eases toward the // cursor when present, with a few slow dust motes. No grid, no lines. function initAmbientGlow(canvasId){ const canvas = document.getElementById(canvasId); if(!canvas) return; const section = canvas.parentElement; const ctx = canvas.getContext('2d'); let w, h; const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; function resize(){ w = canvas.width = canvas.offsetWidth * devicePixelRatio; h = canvas.height = canvas.offsetHeight * devicePixelRatio; } resize(); let pos = {x:w/2,y:h/2}, target = {x:w/2,y:h/2}, hasMouse=false, t=0; section.addEventListener('mousemove', e => { const rect = canvas.getBoundingClientRect(); target.x = (e.clientX-rect.left) * devicePixelRatio; target.y = (e.clientY-rect.top) * devicePixelRatio; hasMouse = true; }); section.addEventListener('mouseleave', () => { hasMouse = false; }); const motes = Array.from({length:16}, () => ({ x: Math.random(), y: Math.random(), r: Math.random()*1.4+0.6, phase: Math.random()*Math.PI*2, speed: 0.15+Math.random()*0.2 })); function draw(){ ctx.clearRect(0,0,w,h); if(!reduceMotion){ t += 0.01; if(!hasMouse){ target.x = w/2 + Math.cos(t)*w*0.18; target.y = h/2 + Math.sin(t*0.8)*h*0.24; } pos.x += (target.x-pos.x)*0.04; pos.y += (target.y-pos.y)*0.04; } else { pos.x = hasMouse ? target.x : w/2; pos.y = hasMouse ? target.y : h/2; } const grad = ctx.createRadialGradient(pos.x,pos.y,0,pos.x,pos.y,280*devicePixelRatio); grad.addColorStop(0,'rgba(255,176,32,0.16)'); grad.addColorStop(0.5,'rgba(255,90,31,0.06)'); grad.addColorStop(1,'rgba(255,90,31,0)'); ctx.fillStyle = grad; ctx.fillRect(0,0,w,h); motes.forEach(m => { const mx = m.x*w + Math.sin(t*m.speed+m.phase)*10*devicePixelRatio; const my = m.y*h + Math.cos(t*m.speed*0.7+m.phase)*10*devicePixelRatio; ctx.beginPath(); ctx.arc(mx,my,m.r*devicePixelRatio,0,Math.PI*2); ctx.fillStyle = 'rgba(216,180,106,0.35)'; ctx.fill(); }); requestAnimationFrame(draw); } window.addEventListener('resize', resize); draw(); } // CTA — lines converging on a single point, visualizing "move onto the fabric" // right where the final action lives. function initConverge(canvasId){ const canvas = document.getElementById(canvasId); if(!canvas) return; const ctx = canvas.getContext('2d'); let w, h; const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; function resize(){ w = canvas.width = canvas.offsetWidth * devicePixelRatio; h = canvas.height = canvas.offsetHeight * devicePixelRatio; } resize(); const count = 22; const lines = Array.from({length:count}, (_,i) => ({ angle: (i/count)*Math.PI*2 + Math.random()*0.15, dist: 0.3 + Math.random()*0.9, speed: 0.0018 + Math.random()*0.0028, len: 0.1 + Math.random()*0.12 })); function draw(){ ctx.clearRect(0,0,w,h); const cx = w/2, cy = h*0.42; const maxR = Math.max(w,h)*0.62; lines.forEach(l => { if(!reduceMotion){ l.dist -= l.speed; if(l.dist < l.len+0.05) l.dist = 1 + Math.random()*0.2; } const r1 = l.dist*maxR, r0 = (l.dist+l.len)*maxR; const x1 = cx+Math.cos(l.angle)*r1, y1 = cy+Math.sin(l.angle)*r1; const x0 = cx+Math.cos(l.angle)*r0, y0 = cy+Math.sin(l.angle)*r0; const grad = ctx.createLinearGradient(x0,y0,x1,y1); grad.addColorStop(0,'rgba(255,90,31,0)'); grad.addColorStop(1,'rgba(255,176,32,0.45)'); ctx.strokeStyle = grad; ctx.lineWidth = 1.3*devicePixelRatio; ctx.beginPath(); ctx.moveTo(x0,y0); ctx.lineTo(x1,y1); ctx.stroke(); }); ctx.beginPath(); ctx.arc(cx,cy,2.6*devicePixelRatio,0,Math.PI*2); ctx.fillStyle = 'rgba(255,176,32,0.75)'; ctx.fill(); requestAnimationFrame(draw); } window.addEventListener('resize', resize); draw(); } // Footer — faint seismograph-style waveform rows, echoing "all shards nominal" // as a steady, quiet pulse rather than an alert. function initWaveLines(canvasId){ const canvas = document.getElementById(canvasId); if(!canvas) return; const ctx = canvas.getContext('2d'); let w, h, t = 0; const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; function resize(){ w = canvas.width = canvas.offsetWidth * devicePixelRatio; h = canvas.height = canvas.offsetHeight * devicePixelRatio; } function draw(){ ctx.clearRect(0,0,w,h); const rows = 4; for(let r=0; r<rows; r++){ const y = h*(0.2 + r*0.22); ctx.beginPath(); ctx.strokeStyle = `rgba(255,90,31,${0.05+r*0.02})`; ctx.lineWidth = 1*devicePixelRatio; for(let x=0; x<=w; x+=4*devicePixelRatio){ const yy = y + Math.sin(x*0.01 + t*0.6 + r*1.3) * (6+r*2)*devicePixelRatio; if(x===0) ctx.moveTo(x,yy); else ctx.lineTo(x,yy); } ctx.stroke(); } if(!reduceMotion) t += 0.02; requestAnimationFrame(draw); } window.addEventListener('resize', resize); resize(); draw(); } initCircuitGrid('processCanvas'); initScanSweep('logosCanvas'); initAmbientGlow('quoteCanvas'); initConverge('ctaCanvas'); initWaveLines('footerCanvas');</script>