{"id":2368,"date":"2026-08-18T13:37:45","date_gmt":"2026-08-18T13:37:45","guid":{"rendered":"https:\/\/smartdata.ece.ufl.edu\/?p=2368"},"modified":"2026-08-18T13:37:46","modified_gmt":"2026-08-18T13:37:46","slug":"continuous-time-convolution-interactive-demo","status":"publish","type":"post","link":"https:\/\/smartdata.ece.ufl.edu\/index.php\/2026\/08\/18\/continuous-time-convolution-interactive-demo\/","title":{"rendered":"Continuous-Time Convolution Interactive Demo"},"content":{"rendered":"\n<pre class=\"wp-block-verse\"><strong>Disclaimer:<\/strong> This was built with the help of ClaudeAI as an educational tool for Foundations of Digital Signal Processing<\/pre>\n\n\n\n<div id=\"conv-root\" style=\"font-family:Georgia,serif;max-width:820px;margin:0 auto;padding:20px;background:#f9f7f2;border:2px solid #c8b89a;border-radius:10px;color:#222;\">\n\n  <h2 style=\"text-align:center;margin:0 0 4px 0;font-size:1.4em;color:#3a2a0a;letter-spacing:1px;\">Convolution Interactive Demo<\/h2>\n  <p style=\"text-align:center;margin:0 0 18px 0;font-size:0.93em;color:#666;\">(f \u2605 g)(t) = \u222b f(\u03c4) \u00b7 g(t \u2212 \u03c4) d\u03c4<\/p>\n\n  <!-- Signal selector row -->\n  <div style=\"display:flex;gap:18px;justify-content:center;flex-wrap:wrap;margin-bottom:16px;\">\n    <div style=\"background:#fff;border:1px solid #c8b89a;border-radius:8px;padding:12px 18px;\">\n      <label style=\"font-size:0.88em;font-weight:bold;color:#5a3a0a;display:block;margin-bottom:6px;\">f(\u03c4) shape<\/label>\n      <select id=\"fShape\" style=\"font-size:0.9em;padding:4px 8px;border:1px solid #aaa;border-radius:5px;background:#fff;\">\n        <option value=\"rect\">Rectangle<\/option>\n        <option value=\"tri\">Triangle<\/option>\n        <option value=\"exp\">Decaying Exp<\/option>\n        <option value=\"gauss\">Gaussian<\/option>\n      <\/select>\n    <\/div>\n    <div style=\"background:#fff;border:1px solid #c8b89a;border-radius:8px;padding:12px 18px;\">\n      <label style=\"font-size:0.88em;font-weight:bold;color:#3a5a0a;display:block;margin-bottom:6px;\">g(\u03c4) shape<\/label>\n      <select id=\"gShape\" style=\"font-size:0.9em;padding:4px 8px;border:1px solid #aaa;border-radius:5px;background:#fff;\">\n        <option value=\"rect\">Rectangle<\/option>\n        <option value=\"tri\">Triangle<\/option>\n        <option value=\"exp\">Decaying Exp<\/option>\n        <option value=\"gauss\">Gaussian<\/option>\n      <\/select>\n    <\/div>\n  <\/div>\n\n  <!-- Slider -->\n  <div style=\"background:#fff;border:1px solid #c8b89a;border-radius:8px;padding:14px 20px;margin-bottom:18px;\">\n    <label style=\"font-size:0.92em;font-weight:bold;color:#222;display:block;margin-bottom:8px;\">\n      Shift: <span id=\"tLabel\" style=\"color:#b03a00;font-family:monospace;\">t = 0.00<\/span>\n    <\/label>\n    <input id=\"tSlider\" type=\"range\" min=\"-3\" max=\"3\" step=\"0.01\" value=\"0\"\n      style=\"width:100%;accent-color:#b03a00;cursor:pointer;\">\n    <div style=\"display:flex;justify-content:space-between;font-size:0.75em;color:#999;margin-top:2px;\">\n      <span>\u22123<\/span><span>0<\/span><span>+3<\/span>\n    <\/div>\n  <\/div>\n\n  <!-- Canvas row -->\n  <div style=\"display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px;\">\n\n    <div style=\"background:#fff;border:1px solid #d0c8b8;border-radius:8px;padding:10px;\">\n      <div style=\"font-size:0.82em;font-weight:bold;color:#5a3a0a;margin-bottom:4px;text-align:center;\">\n        f(\u03c4) &amp; g(t\u2212\u03c4) \u2014 overlap region\n      <\/div>\n      <canvas id=\"cvOverlap\" width=\"360\" height=\"160\" style=\"width:100%;display:block;border-radius:4px;background:#fafafa;\"><\/canvas>\n    <\/div>\n\n    <div style=\"background:#fff;border:1px solid #d0c8b8;border-radius:8px;padding:10px;\">\n      <div style=\"font-size:0.82em;font-weight:bold;color:#1a3a6a;margin-bottom:4px;text-align:center;\">\n        Product f(\u03c4) \u00b7 g(t\u2212\u03c4)\n      <\/div>\n      <canvas id=\"cvProduct\" width=\"360\" height=\"160\" style=\"width:100%;display:block;border-radius:4px;background:#fafafa;\"><\/canvas>\n    <\/div>\n\n  <\/div>\n\n  <!-- Output canvas -->\n  <div style=\"background:#fff;border:1px solid #d0c8b8;border-radius:8px;padding:10px;margin-bottom:14px;\">\n    <div style=\"font-size:0.82em;font-weight:bold;color:#1a5a1a;margin-bottom:4px;text-align:center;\">\n      Output (f \u2605 g)(t) \u2014 running integral (current t marked)\n    <\/div>\n    <canvas id=\"cvOutput\" width=\"740\" height=\"160\" style=\"width:100%;display:block;border-radius:4px;background:#fafafa;\"><\/canvas>\n  <\/div>\n\n  <!-- Legend -->\n  <div style=\"display:flex;flex-wrap:wrap;gap:14px;justify-content:center;font-size:0.8em;\">\n    <span><span style=\"display:inline-block;width:28px;height:4px;background:#c0392b;border-radius:2px;vertical-align:middle;margin-right:5px;\"><\/span>f(\u03c4)<\/span>\n    <span><span style=\"display:inline-block;width:28px;height:4px;background:#27ae60;border-radius:2px;vertical-align:middle;margin-right:5px;\"><\/span>g(t\u2212\u03c4)<\/span>\n    <span><span style=\"display:inline-block;width:28px;height:4px;background:#8e44ad;border-radius:2px;vertical-align:middle;margin-right:5px;\"><\/span>product<\/span>\n    <span><span style=\"display:inline-block;width:28px;height:4px;background:#2471a3;border-radius:2px;vertical-align:middle;margin-right:5px;\"><\/span>(f\u2605g)(t)<\/span>\n    <span><span style=\"display:inline-block;width:10px;height:10px;background:rgba(255,220,100,0.55);border:1px solid #c8a400;border-radius:2px;vertical-align:middle;margin-right:5px;\"><\/span>overlap area<\/span>\n  <\/div>\n\n  <p style=\"font-size:0.77em;color:#888;text-align:center;margin-top:14px;\">\n    Drag the slider to shift g(t\u2212\u03c4) across f(\u03c4). The yellow shaded area is their product; its integral builds the output curve.\n  <\/p>\n\n<\/div>\n\n<script>\n(function(){\n  var N = 600;       \/\/ sample points\n  var TAU_MIN = -3, TAU_MAX = 3;\n  var T_MIN   = -3, T_MAX   = 3;\n\n  function linspace(a,b,n){\n    var arr=[], step=(b-a)\/(n-1);\n    for(var i=0;i<n;i++) arr.push(a+i*step);\n    return arr;\n  }\n  var tauArr = linspace(TAU_MIN, TAU_MAX, N);\n  var dtau   = tauArr[1]-tauArr[0];\n\n  \/\/ \u2500\u2500 Signal definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function evalSignal(shape, tau){\n    switch(shape){\n      case 'rect':  return (tau>=-1 && tau<=1) ? 1 : 0;\n      case 'tri':   return (Math.abs(tau)<1) ? (1-Math.abs(tau)) : 0;\n      case 'exp':   return (tau>=0 && tau<=3) ? Math.exp(-2*tau) : 0;\n      case 'gauss': return Math.exp(-3*tau*tau);\n    }\n    return 0;\n  }\n\n  function getSignalArr(shape){\n    return tauArr.map(function(v){ return evalSignal(shape,v); });\n  }\n\n  \/\/ \u2500\u2500 Convolution (full, precomputed over T) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  var tOut = linspace(T_MIN, T_MAX, N);\n\n  function computeConv(fArr, gShape){\n    return tOut.map(function(t){\n      var sum=0;\n      for(var i=0;i<N;i++){\n        var gval = evalSignal(gShape, t - tauArr[i]);\n        sum += fArr[i]*gval*dtau;\n      }\n      return sum;\n    });\n  }\n\n  \/\/ \u2500\u2500 Canvas helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function clearCanvas(ctx, w, h){\n    ctx.clearRect(0,0,w,h);\n    ctx.fillStyle='#fafafa';\n    ctx.fillRect(0,0,w,h);\n  }\n\n  function drawAxes(ctx, w, h, xMin, xMax, yMin, yMax, label){\n    var mx=40, my=10, mb=22, mr=10;\n    ctx.save();\n    ctx.strokeStyle='#ccc'; ctx.lineWidth=1;\n    \/\/ grid\n    ctx.setLineDash([2,3]);\n    for(var g=-3;g<=3;g++){\n      var gx=mx+(g-xMin)\/(xMax-xMin)*(w-mx-mr);\n      ctx.beginPath(); ctx.moveTo(gx,my); ctx.lineTo(gx,h-mb); ctx.stroke();\n    }\n    ctx.setLineDash([]);\n    \/\/ x-axis\n    ctx.strokeStyle='#888'; ctx.lineWidth=1.5;\n    var y0=my+(0-yMax)\/(yMin-yMax)*(h-my-mb);\n    if(y0<my) y0=my; if(y0>h-mb) y0=h-mb;\n    ctx.beginPath(); ctx.moveTo(mx,y0); ctx.lineTo(w-mr,y0); ctx.stroke();\n    \/\/ y-axis\n    var x0=mx+(0-xMin)\/(xMax-xMin)*(w-mx-mr);\n    if(x0<mx) x0=mx; if(x0>w-mr) x0=w-mr;\n    ctx.beginPath(); ctx.moveTo(x0,my); ctx.lineTo(x0,h-mb); ctx.stroke();\n    \/\/ tick labels\n    ctx.fillStyle='#999'; ctx.font='10px monospace'; ctx.textAlign='center';\n    [-3,-2,-1,0,1,2,3].forEach(function(v){\n      var px=mx+(v-xMin)\/(xMax-xMin)*(w-mx-mr);\n      ctx.fillText(v, px, h-6);\n    });\n    ctx.restore();\n    return {mx:mx,my:my,mb:mb,mr:mr,y0:y0,x0:x0};\n  }\n\n  function toCanvasXY(val, idx, N, ax, w, h, xMin, xMax, yMin, yMax){\n    var px = ax.mx + (tauArr[idx]-xMin)\/(xMax-xMin)*(w-ax.mx-ax.mr);\n    var py = ax.my + (yMax-val)\/(yMax-yMin)*(h-ax.my-ax.mb);\n    return {x:px, y:py};\n  }\n\n  function plotLine(ctx, xArr, yArr, ax, w, h, xMin, xMax, yMin, yMax, color, lw){\n    ctx.save();\n    ctx.strokeStyle=color; ctx.lineWidth=lw||2; ctx.lineJoin='round';\n    ctx.beginPath();\n    var started=false;\n    for(var i=0;i<xArr.length;i++){\n      var px=ax.mx+(xArr[i]-xMin)\/(xMax-xMin)*(w-ax.mx-ax.mr);\n      var py=ax.my+(yMax-yArr[i])\/(yMax-yMin)*(h-ax.my-ax.mb);\n      if(!started){ctx.moveTo(px,py);started=true;}else{ctx.lineTo(px,py);}\n    }\n    ctx.stroke();\n    ctx.restore();\n  }\n\n  \/\/ \u2500\u2500 Main draw \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  var cvOv  = document.getElementById('cvOverlap');\n  var cvPr  = document.getElementById('cvProduct');\n  var cvOut = document.getElementById('cvOutput');\n  var ctxOv = cvOv.getContext('2d');\n  var ctxPr = cvPr.getContext('2d');\n  var ctxOut= cvOut.getContext('2d');\n\n  function draw(t){\n    var fShape = document.getElementById('fShape').value;\n    var gShape = document.getElementById('gShape').value;\n    var fArr   = getSignalArr(fShape);\n    var gShifted = tauArr.map(function(tau){ return evalSignal(gShape, t-tau); });\n    var prod   = fArr.map(function(v,i){ return v*gShifted[i]; });\n    var convOut= computeConv(fArr, gShape);\n    var yMax=1.15, yMin=-0.1;\n\n    \/\/ --- Overlap canvas ---\n    var W=cvOv.width, H=cvOv.height;\n    clearCanvas(ctxOv,W,H);\n    var ax=drawAxes(ctxOv,W,H,-3,3,yMin,yMax);\n    \/\/ shade overlap region\n    ctxOv.save();\n    ctxOv.beginPath();\n    var first=true;\n    for(var i=0;i<N;i++){\n      var shade=Math.min(fArr[i],gShifted[i]);\n      if(shade<0) shade=0;\n      var px=ax.mx+(tauArr[i]-(-3))\/(6)*(W-ax.mx-ax.mr);\n      var py=ax.my+(yMax-shade)\/(yMax-yMin)*(H-ax.my-ax.mb);\n      if(first){ctxOv.moveTo(px,ax.y0);ctxOv.lineTo(px,py);first=false;}\n      else ctxOv.lineTo(px,py);\n    }\n    ctxOv.lineTo(ax.mx+(tauArr[N-1]-(-3))\/(6)*(W-ax.mx-ax.mr), ax.y0);\n    ctxOv.closePath();\n    ctxOv.fillStyle='rgba(255,210,60,0.45)';\n    ctxOv.fill();\n    ctxOv.restore();\n    plotLine(ctxOv,tauArr,fArr,ax,W,H,-3,3,yMin,yMax,'#c0392b',2.2);\n    plotLine(ctxOv,tauArr,gShifted,ax,W,H,-3,3,yMin,yMax,'#27ae60',2.2);\n\n    \/\/ --- Product canvas ---\n    W=cvPr.width; H=cvPr.height;\n    clearCanvas(ctxPr,W,H);\n    ax=drawAxes(ctxPr,W,H,-3,3,yMin,yMax);\n    ctxPr.save();\n    ctxPr.beginPath();\n    first=true;\n    for(var i=0;i<N;i++){\n      var px=ax.mx+(tauArr[i]-(-3))\/(6)*(W-ax.mx-ax.mr);\n      var py=ax.my+(yMax-prod[i])\/(yMax-yMin)*(H-ax.my-ax.mb);\n      if(first){ctxPr.moveTo(px,ax.y0);ctxPr.lineTo(px,py);first=false;}\n      else ctxPr.lineTo(px,py);\n    }\n    ctxPr.lineTo(ax.mx+(tauArr[N-1]-(-3))\/(6)*(W-ax.mx-ax.mr), ax.y0);\n    ctxPr.closePath();\n    ctxPr.fillStyle='rgba(142,68,173,0.2)';\n    ctxPr.fill();\n    ctxPr.restore();\n    plotLine(ctxPr,tauArr,prod,ax,W,H,-3,3,yMin,yMax,'#8e44ad',2.2);\n\n    \/\/ --- Output canvas ---\n    W=cvOut.width; H=cvOut.height;\n    clearCanvas(ctxOut,W,H);\n    var oMax=Math.max.apply(null,convOut.map(Math.abs));\n    var oScale=oMax>0.01?oMax:1;\n    ax=drawAxes(ctxOut,W,H,-3,3,-oScale*0.15,oScale*1.25);\n    plotLine(ctxOut,tOut,convOut,ax,W,H,-3,3,-oScale*0.15,oScale*1.25,'#2471a3',2.5);\n    \/\/ marker for current t\n    var tNorm=(t-(-3))\/6;\n    var markerX=ax.mx+tNorm*(W-ax.mx-ax.mr);\n    ctxOut.save();\n    ctxOut.strokeStyle='#b03a00'; ctxOut.lineWidth=1.5; ctxOut.setLineDash([4,3]);\n    ctxOut.beginPath(); ctxOut.moveTo(markerX,ax.my); ctxOut.lineTo(markerX,H-ax.mb); ctxOut.stroke();\n    \/\/ dot on curve\n    var tIdx=Math.round((t-(-3))\/6*(N-1));\n    tIdx=Math.max(0,Math.min(N-1,tIdx));\n    var dotY=ax.my+(oScale*1.25-convOut[tIdx])\/(oScale*1.25-(-oScale*0.15))*(H-ax.my-ax.mb);\n    ctxOut.setLineDash([]);\n    ctxOut.beginPath();\n    ctxOut.arc(markerX,dotY,5,0,2*Math.PI);\n    ctxOut.fillStyle='#b03a00'; ctxOut.fill();\n    ctxOut.strokeStyle='#fff'; ctxOut.lineWidth=1.5; ctxOut.stroke();\n    ctxOut.restore();\n  }\n\n  var slider = document.getElementById('tSlider');\n  var tLabel = document.getElementById('tLabel');\n\n  function onSlider(){\n    var t=parseFloat(slider.value);\n    tLabel.textContent='t = '+(t>=0?'+':'')+t.toFixed(2);\n    draw(t);\n  }\n\n  slider.addEventListener('input', onSlider);\n  document.getElementById('fShape').addEventListener('change', onSlider);\n  document.getElementById('gShape').addEventListener('change', onSlider);\n\n  \/\/ initial draw\n  onSlider();\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Disclaimer: This was built with the help of ClaudeAI as an educational tool for Foundations of Digital Signal Processing Convolution Interactive Demo [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"post-templates\/post_noheader_nofooter2.php","format":"standard","meta":{"footnotes":""},"categories":[76,108],"tags":[],"class_list":["post-2368","post","type-post","status-publish","format-standard","hentry","category-education","category-foundations-of-digital-signal-processing"],"_links":{"self":[{"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/posts\/2368","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/comments?post=2368"}],"version-history":[{"count":1,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/posts\/2368\/revisions"}],"predecessor-version":[{"id":2369,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/posts\/2368\/revisions\/2369"}],"wp:attachment":[{"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/media?parent=2368"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/categories?post=2368"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartdata.ece.ufl.edu\/index.php\/wp-json\/wp\/v2\/tags?post=2368"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}