Visual Gantt Chart · Drag & Drop · Export PNG
Gantt Chart Generator
Build professional project timelines instantly. Add tasks, set dates, assign colours and export your Gantt chart as a PNG. Pro Mode unlocks milestones, dependencies, progress tracking, team assignment and critical path.
● Pro Version is Free for now
Pro Mode active — milestones, dependencies, progress & critical path unlocked
Add tasks · set dates · export PNG
Privacy Guaranteed — All data stays in your browser. Never sent to any server.
Total Tasks
0
in chart
Duration
0
total days
Avg Progress
0%
completion
End Date
—
project finish
Project:
Zoom:
You can also type the date directly (YYYY-MM-DD)
Gantt Chart
Critical Path Highlighted
Add your first task above to start building your Gantt chart
Save chart as:
Task List
0 tasks
No tasks yet. Add a task above.
Pro Mode is Free — 8 Extra Features Unlocked
Switch to Pro to access everything below at no cost
◆
Milestones
Mark key dates as diamond milestone markers on the chart
📊
Progress Tracking
Add % completion to each task — bars fill to show progress visually
🔗
Dependencies
Link tasks so arrows show which tasks must finish before others start
👤
Team Assignment
Assign each task to a team member — shown in the chart and task list
🔴
Critical Path
Auto-highlights the longest task chain in red so you know what to protect
📅
Today Marker
Red dashed line shows where today falls on your timeline at a glance
🔍
Zoom: Day/Week/Month
Switch between Day, Week and Month views to zoom in or see the big picture
📥
Export CSV · PDF · Word
Download as CSV for Excel/Sheets, PDF for sharing, or a colour-filled Word table
Related Tools
' + esc(t.assignee) + '' : ''; const milestoneStr = t.type === 'milestone' ? ' ◆' : ''; const rowBg = idx % 2 === 0 ? '#fff' : '#f8fafc'; let cells = '
${esc(proj)}
Generated by SmallStudyTools.com | ${new Date().toLocaleDateString()}
smallstudytools.com — Free Online Gantt Chart Generator
`;const blob = new Blob([html], {type: 'text/html;charset=utf-8'}); const a = document.createElement('a'); a.download = 'smallstudytools-gantt.doc'; a.href = URL.createObjectURL(blob); a.click(); toast('Word file downloaded! Open with Microsoft Word or LibreOffice.'); }function exportCSV() { if (!tasks.length) { toast('Add tasks first'); return; } const headers = ['#','Task Name','Start Date','Duration (days)','End Date','Progress %','Assignee','Type','Depends On']; const rows = tasks.map((t,i) => [ i+1, t.name, t.start, t.dur, fmtDate(addDays(parseDate(t.start), t.dur)), t.progress||0, t.assignee||'', t.type||'task', t.dependsOn||'' ]); const csv = [headers, ...rows].map(r => r.map(v => `"${String(v).replace(/"/g,'""')}"`).join(',')).join('\n'); const blob = new Blob([csv], {type:'text/csv'}); const a = document.createElement('a'); a.download = 'smallstudytools-gantt.csv'; a.href = URL.createObjectURL(blob); a.click(); toast('CSV exported!'); }// ── ZOOM (PRO) ───────────────────────────────────────────────── function setZoom(z, btn) { zoom = z; document.querySelectorAll('.zoom-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); redraw(); }// ── SAMPLE DATA ──────────────────────────────────────────────── function loadSample() { const today = new Date(); const base = fmtDate(today); const d = (n) => fmtDate(addDays(today, n)); try { document.getElementById('fStart')._flatpickr && document.getElementById('fStart')._flatpickr.setDate(today); } catch(e) {} tasks = [ {id:1,name:'Research & Planning',start:base,dur:5,color:'#1a7fc1',progress:100,assignee:'Alice',type:'task',dependsOn:null}, {id:2,name:'Design Phase',start:d(5),dur:7,color:'#7c3aed',progress:60,assignee:'Bob',type:'task',dependsOn:1}, {id:3,name:'Development',start:d(10),dur:14,color:'#10b981',progress:30,assignee:'Alice',type:'task',dependsOn:2}, {id:4,name:'Testing',start:d(22),dur:5,color:'#f59e0b',progress:0,assignee:'Carol',type:'task',dependsOn:3}, {id:5,name:'Launch',start:d(27),dur:1,color:'#ef4444',progress:0,assignee:'',type:'milestone',dependsOn:4}, ]; save(); redraw(); renderTaskList(); toast('✓ Sample loaded — 5 tasks added! Try switching to Pro for milestones, progress & more.'); }// ── ACTIONS ──────────────────────────────────────────────────── function clearAll() { if (!tasks.length) return; tasks = []; save(); redraw(); renderTaskList(); toast('Cleared!'); }// ── MODE ─────────────────────────────────────────────────────── function setMode(mode) { isPro = mode === 'pro'; document.getElementById('btnSimple').classList.toggle('active', !isPro); document.getElementById('btnPro').classList.toggle('active', isPro); document.body.classList.toggle('pro-mode', isPro); document.getElementById('proModeBar').style.display = isPro ? 'flex' : 'none'; document.getElementById('modeHint').textContent = isPro ? 'Milestones · Dependencies · Progress · Team · Critical Path · Today Line · Zoom · CSV Export' : 'Add tasks · set dates · export PNG'; document.getElementById('formGrid').className = isPro ? 'form-grid-pro' : 'form-grid'; redraw(); renderTaskList(); try { localStorage.setItem('sst_gantt_mode', mode); } catch(e) {} }// ── UTILS ────────────────────────────────────────────────────── function esc(s) { return (s||'').replace(/&/g,'&').replace(//g,'>'); } function save() { try { localStorage.setItem(SK, JSON.stringify(tasks)); } catch(e) {} } function toast(msg) { const t=document.getElementById('toast'); t.textContent=msg; t.classList.add('show'); setTimeout(()=>t.classList.remove('show'), 2200); }// ── INIT ─────────────────────────────────────────────────────── try { if (typeof flatpickr === 'function') { flatpickr('#fStart', { dateFormat: 'Y-m-d', altInput: true, altFormat: 'd M Y', defaultDate: new Date(), disableMobile: true, allowInput: true, }); } else { document.getElementById('dateFallbackNote').classList.add('show'); } } catch (e) { document.getElementById('dateFallbackNote').classList.add('show'); }try { const saved = JSON.parse(localStorage.getItem(SK) || '[]'); if (Array.isArray(saved) && saved.length) { tasks = saved; redraw(); renderTaskList(); } const savedMode = localStorage.getItem('sst_gantt_mode'); if (savedMode === 'pro') setMode('pro'); } catch(e) {}