<title>IUE — Graduate Flow</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family:'Inter',sans-serif;
background:#f5f0e8;
color:#1a1a1a;
min-height:100vh;
}
.glass {
background:rgba(255,255,255,0.04);
backdrop-filter:blur(18px);
border:1px solid rgba(255,255,255,0.08);
}
.swiss-border {
border-left:4px solid #f37021;
padding-left:1rem;
}
.glow {
position:fixed;
width:500px; height:500px;
background:#f37021;
border-radius:9999px;
filter:blur(180px);
opacity:.10;
pointer-events:none;
}
#tip {
position:fixed;
background:rgba(26,26,26,0.98);
color:#fff;
padding:10px 14px;
border-radius:8px;
font-size:12px;
pointer-events:none;
display:none;
z-index:9999;
box-shadow:0 5px 15px rgba(0,0,0,0.3);
font-family:sans-serif;
line-height:1.4;
}
</style>
İzmir University of Economics
Graduate Flow
Sankey diagram showing the distribution of graduates from each academic year into departments. Hover over a ribbon to explore.
<script>
const rawFlow = {
2017:{Arch:66,FD:15,ID:31,IAED:54,VCD:15},
2018:{Arch:83,FD:13,ID:20,IAED:44,VCD:12},
2019:{Arch:77,FD:10,ID:15,IAED:54,VCD:14},
2020:{Arch:88,FD:15,ID:24,IAED:73,VCD:16},
2021:{Arch:63,FD:17,ID:27,IAED:46,VCD:18},
2022:{Arch:49,FD:6, ID:15,IAED:50,VCD:12},
2023:{Arch:47,FD:15,ID:29,IAED:47,VCD:19},
2024:{Arch:41,FD:16,ID:32,IAED:56,VCD:8 },
2025:{Arch:35,FD:12,ID:46,IAED:52,VCD:18},
};
const skDepts = [
{ key:'Arch', label:'Architecture', color:'#66438b' },
{ key:'IAED', label:'Interior Architecture', color:'#57ba47' },
{ key:'ID', label:'Industrial Design', color:'#f4d911' },
{ key:'VCD', label:'Visual Communication Design', color:'#f37235' },
{ key:'FD', label:'Fashion Design', color:'#d1428e' }
];
const skYears = Object.keys(rawFlow).map(Number).sort((a,b)=>a-b);
const skYearTotals = {};
skYears.forEach(y=>{ skYearTotals[y]=skDepts.reduce((s,d)=>s+rawFlow[y][d.key],0); });
const skDeptTotals = {};
skDepts.forEach(d=>{ skDeptTotals[d.label]=skYears.reduce((s,y)=>s+rawFlow[y][d.key],0); });
const skCanvas = document.getElementById('sk');
const skTip = document.getElementById('tip');
const DPR = window.devicePixelRatio||1;
const PAD_TOP=20, PAD_BTM=20, PAD_BAND=14, BAR_W=18;
function getSkLayout(){
const cssW = skCanvas.parentElement.clientWidth;
const H = 500;
const lx=60, rx=cssW-220;
const totalAll = skYears.reduce((s,y)=>s+skYearTotals[y],0);
let yOff=PAD_TOP;
const yearPos={};
skYears.forEach(y=>{
const h=(skYearTotals[y]/totalAll)*(H-PAD_TOP-PAD_BTM-PAD_BAND*(skYears.length-1));
yearPos[y]={y:yOff,h}; yOff+=h+PAD_BAND;
});
let dOff=PAD_TOP;
const deptPos={};
skDepts.forEach(d=>{
const h=(skDeptTotals[d.label]/totalAll)*(H-PAD_TOP-PAD_BTM-PAD_BAND*(skDepts.length-1));
deptPos[d.label]={y:dOff,h}; dOff+=h+PAD_BAND;
});
return{cssW,H,lx,rx,yearPos,deptPos};
}
function buildRibbons(layout){
const ribbons=[];
const yearFill={}; skYears.forEach(y=>yearFill[y]=0);
const deptFill={}; skDepts.forEach(d=>deptFill[d.label]=0);
skYears.forEach(y=>{
const scale=layout.yearPos[y].h/skYearTotals[y];
skDepts.forEach(d=>{
const val=rawFlow[y][d.key];
if(val<=0)return;
const fhY=val*scale;
const fhD=val*(layout.deptPos[d.label].h/skDeptTotals[d.label]);
ribbons.push({year:y,dept:d.label,val,color:d.color,
y1s:layout.yearPos[y].y+yearFill[y],fhY,
y2s:layout.deptPos[d.label].y+deptFill[d.label],fhD});
yearFill[y]+=fhY;
deptFill[d.label]+=fhD;
});
});
return ribbons;
}
let skLayout, skRibbons, skCtx;
function initSankey(){
skLayout=getSkLayout();
skCanvas.style.width=skLayout.cssW+'px';
skCanvas.style.height=skLayout.H+'px';
skCanvas.width=Math.round(skLayout.cssW*DPR);
skCanvas.height=Math.round(skLayout.H*DPR);
skCtx=skCanvas.getContext('2d');
skCtx.scale(DPR,DPR);
skRibbons=buildRibbons(skLayout);
drawSankey(null);
}
function drawSankey(hit){
skCtx.clearRect(0,0,skLayout.cssW,skLayout.H);
const cp=(skLayout.rx-(skLayout.lx+BAR_W))*0.45;
skRibbons.forEach(r=>{
const isHit=hit===r;
const alpha=hit?(isHit?'ee':'12'):'90';
skCtx.beginPath();
skCtx.moveTo(skLayout.lx+BAR_W,r.y1s);
skCtx.bezierCurveTo(skLayout.lx+BAR_W+cp,r.y1s,skLayout.rx-cp,r.y2s,skLayout.rx,r.y2s);
skCtx.lineTo(skLayout.rx,r.y2s+r.fhD);
skCtx.bezierCurveTo(skLayout.rx-cp,r.y2s+r.fhD,skLayout.lx+BAR_W+cp,r.y1s+r.fhY,skLayout.lx+BAR_W,r.y1s+r.fhY);
skCtx.fillStyle=r.color+alpha;
skCtx.fill();
});
skYears.forEach(y=>{
const p=skLayout.yearPos[y];
skCtx.fillStyle='#1a1a1a';
skCtx.fillRect(skLayout.lx,p.y,BAR_W,p.h);
skCtx.textAlign='right';
skCtx.font='600 11px sans-serif';
skCtx.fillStyle='#1a1a1a';
skCtx.fillText(y,skLayout.lx-8,p.y+p.h/2+4);
});
skDepts.forEach(d=>{
const p=skLayout.deptPos[d.label];
skCtx.fillStyle=d.color;
skCtx.fillRect(skLayout.rx,p.y,BAR_W,p.h);
skCtx.textAlign='left';
skCtx.fillStyle='#1a1a1a';
skCtx.font='500 11px sans-serif';
skCtx.fillText(d.label,skLayout.rx+BAR_W+8,p.y+p.h/2+4);
});
}
skCanvas.addEventListener('mousemove',e=>{
const rect=skCanvas.getBoundingClientRect();
const mx=(e.clientX-rect.left)*(skLayout.cssW/rect.width);
const my=(e.clientY-rect.top)*(skLayout.H/rect.height);
const getBezierY=(y1,y2,t)=>{const mt=1-t;return mt*mt*mt*y1+3*mt*mt*t*y1+3*mt*t*t*y2+t*t*t*y2;};
let currentHit=skRibbons.find(r=>{
if(mxskLayout.rx)return false;
const t=(mx-(skLayout.lx+BAR_W))/(skLayout.rx-(skLayout.lx+BAR_W));
return my>=getBezierY(r.y1s,r.y2s,t)&&my<=getBezierY(r.y1s+r.fhY,r.y2s+r.fhD,t);
});
if(currentHit){
const pct=((currentHit.val/skYearTotals[currentHit.year])*100).toFixed(1);
skTip.style.display='block';
skTip.style.left=e.clientX+15+'px';
skTip.style.top=e.clientY+15+'px';
skTip.innerHTML=`
${currentHit.year}${currentHit.dept}:
${currentHit.val} (${pct}%)`;
} else { skTip.style.display='none'; }
drawSankey(currentHit);
});
skCanvas.addEventListener('mouseleave',()=>{
skTip.style.display='none';
drawSankey(null);
});
window.addEventListener('resize',initSankey);
initSankey();
</script>
Graduate Flow
Sankey diagram showing the distribution of graduates from each academic year into departments. Hover over a ribbon to explore.
Year → Department Flow
${currentHit.dept}: ${currentHit.val} (${pct}%)`; } else { skTip.style.display='none'; } drawSankey(currentHit); }); skCanvas.addEventListener('mouseleave',()=>{ skTip.style.display='none'; drawSankey(null); }); window.addEventListener('resize',initSankey); initSankey(); </script>