. 3D Pie Chart SVG JavaScript – Free Interactive | Coodeverse Skip to main content

Animated 3D Pie Chart – Free Interactive SVG JavaScript Data Visualization Component

The Animated 3D Pie Chart is a free, copy-paste ready interactive data visualization built with pure SVG and vanilla JavaScript — zero external libraries, no Chart.js, no D3. It renders a 6-slice 3D pie chart displaying budget data: Rent 38%, Groceries 18%, Utilities 10%, Transport 9%, Savings 15%, Entertainment 10%. The 3D effect is achieved with CSS transform: rotateX(50deg) rotateZ(-15deg) on the SVG inside a container with perspective: 1000px and transform-style: preserve-3d. Each slice has a 3D side depth panel drawn as a second SVG path at cy + depth * 0.82 offset, and a linearGradient fill on the top face.

Slices animate in with a staggered entrance: opacity transitions from 0 to 1 starting at 200ms with i * 120ms delay per slice. Hovering a slice adds the .pulled CSS class which applies transform: translate3d(var(--tx), var(--ty), 0) — the --tx and --ty values are calculated from the slice midpoint angle to push it 18px radially outward. Clicking locks the pull-out state. Percentage labels are SVG <text> elements positioned at 66% of the radius. A live data update fires after 3.8 seconds via setTimeout, calling draw() with new values and re-animating the entire chart.

What's Included in the Download

How the SVG Arc Math Works

Each slice is an SVG <path>. The polar() function converts degree angles to SVG x/y coordinates: a = (a-90) * Math.PI/180 (offset so 0° points up), then x = cx + r * Math.cos(a), y = cy + r * Math.sin(a). The arc path string is: M cx,cy L startX,startY A radius,radius 0 large-arc-flag 0 endX,endY Z. The large-arc-flag is 0 when the slice is ≤ 180° and 1 for larger slices — this is the key SVG rule for rendering the correct arc direction.

How to Use in a Real Dashboard

Replace the hardcoded data array with values from your API. Call draw(yourData) whenever data changes — on fetch() response, WebSocket message, or user filter. Each call wipes svg.innerHTML and rebuilds with fresh animations. To add more or fewer slices, simply add/remove objects from the array. The HSL color generator automatically spreads colors across 320 degrees of the color wheel. Change the CSS --size variable from 520px to resize. Change depth from 28 to increase or decrease the 3D extrusion thickness.

Best Use Cases

This component is ideal for: analytics dashboards, budget and finance reports, admin panels, data journalism visuals, presentation slides, SaaS product metrics pages, portfolio data sections, and any project needing a premium chart without Chart.js or D3 dependencies. Replace the budget categories with sales data, traffic sources, survey results, or any proportional dataset.

License

Released under Creative Commons Attribution 4.0 International (CC BY 4.0). Free for personal and commercial use with attribution to Coodeverse.

Source Code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Animated 3D Pie Chart</title>
  <style>
    :root{--size:520px;--depth:28;--bg:#0f1724}
    *{box-sizing:border-box}
    body{margin:0;min-height:100vh;display:grid;place-items:center;background:linear-gradient(180deg,var(--bg) 0%,#071025 100%);color:#e6eef8}
    .card{width:calc(var(--size)+240px);max-width:100%;background:linear-gradient(180deg,rgba(255,255,255,.02),rgba(255,255,255,.01));border-radius:14px;padding:20px;box-shadow:0 10px 30px rgba(2,6,23,.6);display:flex;gap:20px;align-items:center}
    .chart-wrap{width:var(--size);height:var(--size);display:grid;place-items:center;perspective:1000px}
    .scene{width:var(--size);height:var(--size);transform-style:preserve-3d;transition:transform .6s cubic-bezier(.2,.9,.3,1)}
    svg{width:100%;height:100%;transform:rotateX(50deg) rotateZ(-15deg)}
    .slices{transform-origin:50% 50%}
    .chart-wrap:hover .scene{transform:translateY(-8px)}
    .slice-top{transition:transform .32s cubic-bezier(.2,.9,.3,1),filter .22s;cursor:pointer}
    .slice-top:hover{filter:brightness(1.08) saturate(1.06)}
    .pulled{transform:translate3d(var(--tx),var(--ty),0)!important}
    .legend{width:200px;display:flex;flex-direction:column;gap:10px;padding:8px 14px}
    .row{display:flex;align-items:center;gap:10px}
    .swatch{width:14px;height:14px;border-radius:3px;box-shadow:0 2px 6px rgba(0,0,0,.6)}
    .lbl{font-size:13px;color:#cfe6ff}
    .callout{font-weight:600;font-size:13px;fill:#fff;text-shadow:0 2px 10px rgba(0,17,29,.5)}
    @media(max-width:860px){.card{flex-direction:column;padding:14px}.legend{width:100%;flex-direction:row;flex-wrap:wrap;justify-content:center;gap:8px}}
  </style>
</head>
<body>
  <div class="card">
    <div class="chart-wrap" role="img" aria-label="3D animated pie chart">
      <div class="scene">
        <svg id="pieSvg" viewBox="0 0 420 420" xmlns="http://www.w3.org/2000/svg"></svg>
      </div>
    </div>
    <div class="legend" id="legend"></div>
  </div>

  <script>
    const data=[{label:"Rent",value:38},{label:"Groceries",value:18},{label:"Utilities",value:10},{label:"Transport",value:9},{label:"Savings",value:15},{label:"Entertainment",value:10}];
    const size=420,cx=size/2,cy=size/2,radius=120,depth=28;
    const svg=document.getElementById("pieSvg"),legend=document.getElementById("legend");

    function hue(i,n){return Math.round((i/n)*320+20)}
    function niceColor(i,n){return`hsl(${hue(i,n)}deg 78% 58%)`}

    function polar(cx,cy,r,a){a=(a-90)*Math.PI/180;return{x:cx+r*Math.cos(a),y:cy+r*Math.sin(a)}}
    function arc(cx,cy,r,s,e){const S=polar(cx,cy,r,e),E=polar(cx,cy,r,s),large=e-s<=180?0:1;
      return`M${cx},${cy} L${E.x},${E.y} A${r},${r} 0 ${large} 0 ${S.x},${S.y} Z`}

    function gradient(id,color){
      let defs=svg.querySelector("defs")||svg.appendChild(document.createElementNS(svg.namespaceURI,"defs"));
      const g=document.createElementNS(svg.namespaceURI,"linearGradient");g.id=id;
      g.innerHTML=`<stop offset="0%" stop-color="${color}"/><stop offset="100%" stop-color="rgba(0,0,0,.12)"/>`;
      defs.appendChild(g);return`url(#${id})`}

    function slice(i,start,end,color,pct){
      const mid=(start+end)/2,id=`s${i}`;
      const top=document.createElementNS(svg.namespaceURI,"path");
      top.setAttribute("d",arc(cx,cy,radius,start,end));
      top.classList.add("slice-top");top.style.fill=gradient(id+"g",color);

      const oy=depth*0.82;
      const p1=polar(cx,cy,radius,start),p2=polar(cx,cy,radius,end);
      const side=document.createElementNS(svg.namespaceURI,"path");
      side.setAttribute("d",`M${p1.x},${p1.y} A${radius},${radius} 0 ${end-start<=180?0:1} 0 ${p2.x},${p2.y} L${p2.x},${p2.y+oy} A${radius},${radius} 0 ${end-start<=180?0:1} 1 ${p1.x},${p1.y+oy} Z`);
      side.style.fill="rgba(0,0,0,.12)";

      const label=document.createElementNS(svg.namespaceURI,"text");
      const lp=polar(cx,cy,radius*0.66,mid);
      label.setAttribute("x",lp.x);label.setAttribute("y",lp.y+oy*0.2);
      label.setAttribute("text-anchor","middle");label.classList.add("callout");
      label.textContent=Math.round(pct)+"%";

      const g=document.createElementNS(svg.namespaceURI,"g");
      g.append(side,top,label);svg.appendChild(g);
      g.style.opacity=0;
      setTimeout(()=>{g.style.transition="transform .52s cubic-bezier(.2,.9,.3,1), opacity .42s";g.style.opacity=1;label.style.opacity=1},200+i*120);

      let pulled=false;
      const pull=()=>{const rad=(mid-90)*Math.PI/180;
        g.style.setProperty("--tx",Math.cos(rad)*18+"px");
        g.style.setProperty("--ty",Math.sin(rad)*18*0.6+"px");
        g.classList.toggle("pulled");pulled=!pulled;}
      top.addEventListener("mouseenter",()=>g.classList.add("pulled"));
      top.addEventListener("mouseleave",()=>g.classList.remove("pulled"));
      top.addEventListener("click",pull);
    }

    function draw(d){
      svg.innerHTML="";legend.innerHTML="";
      const total=d.reduce((a,b)=>a+b.value,0);
      let angle=0;
      const shadow=document.createElementNS(svg.namespaceURI,"ellipse");
      shadow.setAttribute("cx",cx);shadow.setAttribute("cy",cy+depth+36);
      shadow.setAttribute("rx",radius*1.25);shadow.setAttribute("ry",16);
      shadow.style.fill="rgba(0,0,0,.22)";shadow.style.filter="blur(8px)";
      svg.appendChild(shadow);

      d.forEach((item,i)=>{
        const seg=(item.value/total)*360;
        slice(i,angle,angle+seg,niceColor(i,d.length),(item.value/total)*100);
        const row=document.createElement("div");row.className="row";
        const sw=document.createElement("div");sw.className="swatch";sw.style.background=niceColor(i,d.length);
        const txt=document.createElement("div");txt.className="lbl";txt.textContent=`${item.label} — ${item.value}`;
        row.append(sw,txt);legend.appendChild(row);
        angle+=seg;
      });
    }
    draw(data);

    setTimeout(()=>{draw([
      {label:"Rent",value:35},{label:"Groceries",value:20},{label:"Utilities",value:8},
      {label:"Transport",value:7},{label:"Savings",value:20},{label:"Entertainment",value:10}
    ])},3800);
  </script>
</body>
</html>

Frequently Asked Questions

Common questions about the free Animated 3D Pie Chart SVG JavaScript component.

Yes — 100% free under Creative Commons Attribution 4.0. No sign-up, no payment. Click Download Project and use it in personal, commercial, or dashboard projects with attribution to Coodeverse.

The 3D effect uses transform: rotateX(50deg) rotateZ(-15deg) on the SVG inside a container with perspective: 1000px and transform-style: preserve-3d. Each slice has a shadow side panel drawn at cy + depth * 0.82 offset with fill: rgba(0,0,0,.12). Slice arcs use polar-to-Cartesian math: x = cx + r*Math.cos(a), y = cy + r*Math.sin(a) with a = (degrees-90) * Math.PI/180.

On mouseenter the .pulled class is added to the slice group; on mouseleave it's removed. On click, it's toggled for sticky state. .pulled applies transform: translate3d(var(--tx), var(--ty), 0). The --tx and --ty CSS variables are calculated from the slice midpoint angle: Math.cos(rad)*18+'px' and Math.sin(rad)*18*0.6+'px', pushing the slice 18px radially outward.

The path string is: M cx,cy L startX,startY A radius,radius 0 large-arc-flag 0 endX,endY Z. The large-arc-flag is 0 when the slice is ≤ 180° and 1 for larger slices — this is the critical SVG rule. Start/end points come from the polar() function: a = (degrees-90) * Math.PI/180 offsets so 0° points up, then standard cos/sin conversion.

Call draw(newData) with a new array of {label, value} objects. It clears svg.innerHTML and legend.innerHTML then rebuilds everything with fresh staggered animations. In the demo, setTimeout(()=>draw([...]), 3800) triggers after 3.8s. For real dashboards, call draw() on fetch() responses or WebSocket messages.

Edit the data array — each object has label and value. Add or remove objects freely. Colors are auto-generated with hsl(hue, 78%, 58%) spread across 320° of the color wheel. To use custom colors, add a color field to each object and pass it to slice(). Change radius (default 120) or depth (default 28) to resize or adjust 3D thickness.

Browse the full collection at coodeverse.com/projects — VORTEX Interactive Canvas, Realistic Cursor Dragon, NEXUS 3D Pure CSS, Holographic Login and more. All 100% free with complete HTML CSS JavaScript source code.