Build your own medical timeline from a chronology export
Short version: export your cited chronology to a spreadsheet, keep 6 columns (start, end, lane, label, page, note), save as CSV, and paste it into the HTML file below. It draws lanes, duration bars, gaps over your threshold and a key date line, and lists undated rows. It runs in your browser from your own disk, so no patient data is uploaded anywhere.
Limits: Tool facts come from each vendor's own website, read 2026-11-12; we have not used those products. The case on this site is synthetic, with no real patient.
When to draw your own
Most record review tools give you a cited table, not a drawn timeline (see the tools page). Drawing your own makes sense when:
- You want 1 figure for a report or a peer review summary, and the tool has no drawn view.
- You want lanes the tool doesn't offer, such as 1 lane per treating provider.
- You need a figure from a de-identified version of the case for teaching.
Don't draw your own if you'd be retyping dates from a PDF. Retyping is where errors start. Work from an export (Excel, CSV or Word table) of rows that already carry page cites.
The 6 columns
| Column | What goes in it | Rule |
|---|---|---|
start | Date of service, YYYY-MM-DD | Leave empty for undated records; they go to a list, not the chart |
end | End of a span (drug, admission, work status) | Empty for single events. If the end is unknown, leave it empty and say so in note |
lane | Visits, Imaging, Procedures, Medications, Work status, or 1 lane per provider | 4 to 7 lanes. Same spelling every row |
label | Short name: "Lumbar MRI", "Gabapentin 600 mg" | No commas, since the template splits on them |
page | Page cite in the file | Every row has 1. A row without a page doesn't go in |
note | Anything the reader should know | "end not recorded", "signed 9 days later" |
Split a dose change into 2 rows that meet on the change date. Use 1 row per same-day event, so a day with 3 visits draws 3 marks you can hover.
The template: 1 HTML file, no upload
Copy the code into a text file, save it as timeline.html, and open it in your browser. It needs no internet connection and stores nothing. The sample rows come from our synthetic case (no real patient).
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>Offline medical timeline</title>
<style>
body{font:14px system-ui,sans-serif;margin:24px;color:#1d2321;background:#fff}
textarea{width:100%;height:160px;font:12px monospace}
svg text{font:11px system-ui,sans-serif}
</style></head><body>
<h1>Offline medical timeline</h1>
<p>Paste CSV with the header row below, then press Draw. Nothing leaves this file.</p>
<textarea id="csv">start,end,lane,label,page,note
2024-03-04,,Visits,ED visit,12,
2024-03-04,2024-04-15,Medications,Ibuprofen 600 mg,13,
2024-05-06,,Imaging,Lumbar MRI,58,
2024-07-15,,Procedures,ESI 2,97,
2024-11-04,,Visits,PCP visit,112,
,,Undated,Pain diagram,207,no date on page</textarea>
<p><label>Key date <input id="key" value="2024-03-04"></label>
<label>Flag gaps over <input id="gap" type="number" value="60"> days</label>
<button id="go">Draw</button></p>
<svg id="out" width="100%" viewBox="0 0 900 300" role="img" aria-label="Timeline"></svg>
<ul id="undated"></ul>
<script>
var NS="http://www.w3.org/2000/svg";
function day(s){return Date.UTC(+s.slice(0,4),+s.slice(5,7)-1,+s.slice(8,10))/864e5}
function el(t,a,txt){var n=document.createElementNS(NS,t);for(var k in a)n.setAttribute(k,a[k]);if(txt)n.textContent=txt;return n}
function draw(){
var lines=document.getElementById("csv").value.trim().split("\n").slice(1);
var rows=lines.map(function(l){var c=l.split(",");return {s:c[0],e:c[1],lane:c[2],label:c[3],page:c[4]}});
var dated=rows.filter(function(r){return r.s}),und=rows.filter(function(r){return !r.s});
var svg=document.getElementById("out");svg.innerHTML="";
var lanes=[];dated.forEach(function(r){if(lanes.indexOf(r.lane)<0)lanes.push(r.lane)});
var lo=Math.min.apply(0,dated.map(function(r){return day(r.s)}))-7;
var hi=Math.max.apply(0,dated.map(function(r){return day(r.e||r.s)}))+7;
var x=function(d){return 130+(d-lo)/(hi-lo)*750};
var H=40+lanes.length*40;svg.setAttribute("viewBox","0 0 900 "+(H+20));
lanes.forEach(function(l,i){svg.appendChild(el("rect",{x:130,y:30+i*40,width:750,height:32,fill:"#eef3f1"}));
svg.appendChild(el("text",{x:4,y:50+i*40},l))});
var contact=dated.map(function(r){return day(r.s)}).sort(function(a,b){return a-b});
var g=+document.getElementById("gap").value;
for(var i=1;i<contact.length;i++){var d=contact[i]-contact[i-1];if(d>g){
svg.appendChild(el("rect",{x:x(contact[i-1]),y:24,width:x(contact[i])-x(contact[i-1]),height:H-20,fill:"#f4c542","fill-opacity":".3"}));
svg.appendChild(el("text",{x:x(contact[i-1])+4,y:H+12},d+" days"))}}
var k=document.getElementById("key").value;if(k){var kx=x(day(k));
svg.appendChild(el("line",{x1:kx,x2:kx,y1:20,y2:H,stroke:"#1d2321","stroke-dasharray":"4 3"}))}
dated.forEach(function(r){var y=30+lanes.indexOf(r.lane)*40,m;
if(r.e){m=el("rect",{x:x(day(r.s)),y:y+9,width:Math.max(3,x(day(r.e))-x(day(r.s))),height:14,rx:3,fill:"#2e7d32"})}
else{m=el("circle",{cx:x(day(r.s)),cy:y+16,r:6,fill:"#1565c0"})}
m.appendChild(el("title",{},r.s+(r.e?" to "+r.e:"")+": "+r.label+", p. "+r.page));svg.appendChild(m)});
var u=document.getElementById("undated");u.innerHTML="";
und.forEach(function(r){var li=document.createElement("li");li.textContent="Undated: "+r.label+", p. "+r.page;u.appendChild(li)});
}
document.getElementById("go").onclick=draw;draw();
</script></body></html>
Template written for this site on 2026-11-12. Free to copy and change. It handles simple CSV only: no commas or quotes inside fields.
Steps from export to figure
- Export the cited table. From your review tool, export to Excel or CSV. If you only get Word or PDF, copy the table into a spreadsheet.
- Trim to 6 columns. Keep start, end, lane, label, page and note. Drop narrative text; the figure needs labels, not paragraphs.
- Fix spans. Give every medication and work-status row an end date, or leave it empty and write "end not recorded" in the note.
- Save as CSV and paste. Open
timeline.html, paste over the sample rows, set the key date and gap threshold, and press Draw. - Check it. Run the 12-point checklist, then print in grayscale to confirm lanes still read.
- Save the figure with its table. Print to PDF and attach the CSV or the cited table, so every mark can be traced.
Other ways to draw it, and the privacy catch
| Option | Good for | Privacy catch |
|---|---|---|
| This template | Lanes, bars, gaps, key date; offline | None: the file never leaves your machine |
| Excel stacked bar chart | Duration bars (a hidden first series as the offset) | Fine on a covered device; no swimlane labels without work |
| Lucen Timeline (formerly Office Timeline) | Polished PowerPoint figure with swimlanes; imports from Excel | A project tool; keep it to de-identified data unless your IT covers it |
| Preceden | Quick web timelines; free plan allows 10 events per timeline | Web service with no BAA described; de-identified data only |
| TimelineJS | Slide-style interactive story | Requires publishing the Google Sheet to the web: never with patient data |
Tool facts from each vendor's site, read 2026-11-12. Sources are on the tools page.
Questions about drawing a medical timeline
Can I make a medical timeline in Excel?
Yes, as a stacked bar chart: 1 hidden series for the start date and 1 visible series for the duration. It handles bars well and single events poorly. The HTML template above does both.
Is it safe to use an online timeline maker with patient records?
Only if the service signs a BAA with you. The general timeline makers we looked at don't describe 1. Use de-identified data or an offline file.
What date should a timeline use for a late-signed note?
The date of service. Put the signing date in the note column if the delay matters.