A flexible, zero-dependency JavaScript library for creating circular timeline visualizations.
Perfect for annual planning, project timelines, and cyclical data. Built with pure JavaScript and SVG.
📺 Live Demo | 📦 npm | ⭐ GitHub
Circular layouts excel at showing:
Cyclical patterns - Annual processes, seasonal data
Year-at-a-glance - See the entire year in one view
Multiple layers - Compare different event types side-by-side
Space efficiency - Fit more information in less screen space
Traditional linear timelines are better for sequences and chronology. Circular timelines are better for cycles and patterns.
Minimal Example (3 lines)
import CircularTimeline from 'circalify' ;
const timeline = new CircularTimeline ( '#timeline' , {
startYear : 2025 ,
rings : [ { type : 'calendar' } ]
} ) ;
import CircularTimeline from 'circalify' ;
const timeline = new CircularTimeline ( '#timeline' , {
startYear : 2025 ,
rings : [
{ type : 'calendar' } ,
{ type : 'data' , name : 'Events' }
]
} ) ;
timeline . setData ( [
{ label : 'Launch' , startDate : '2025-03-15' , endDate : '2025-03-15' }
] , 'Events' ) ;
Using CDN (No Build Step)
<!DOCTYPE html>
< html >
< head >
< style >
# timeline { width : 100% ; height : 600px ; }
</ style >
</ head >
< body >
< div id ="timeline "> </ div >
< script type ="module ">
import CircularTimeline from 'https://unpkg.com/circalify@latest/src/index.js' ;
const timeline = new CircularTimeline ( '#timeline' , {
startYear : 2025 ,
rings : [
{ type : 'calendar' , calendarType : 'month-names' } ,
{ type : 'data' , name : 'Events' }
]
} ) ;
timeline . setData ( [
{ label : 'Project Launch' , startDate : '2025-03-15' , endDate : '2025-03-15' }
] , 'Events' ) ;
</ script >
</ body >
</ html >
Circalify creates SVG-based circular timelines with:
Flexible rings - Calendar, headers, and data layers
Full styling control - Colors, fonts, sizes
Smart date positioning - Automatic geometry calculations
Interactive - Hover effects and click handlers
Zero dependencies - Pure JavaScript + SVG
Tiny footprint - Lightweight and fast
{
startYear : 2025 , // Required: Year to display
startMonth : 0 , // Optional: Starting month (0-11, default: 0)
numberOfMonths : 12 , // Optional: How many months to show (default: 12)
sameRingHeight : false , // Optional: Equal ring heights (default: false)
backgroundColor : '#fff' , // Optional: Background color (default: '#ffffff')
interactive : true // Optional: Enable hover/click (default: true)
}
Displays time divisions (months, weeks, days, quarters).
{
type : 'calendar' ,
calendarType : 'month-names' , // Options: 'month-names', 'weeks', 'days', 'quarters'
active : true ,
color : '#f0f0f0' ,
height : 18 ,
fontSize : 11 ,
fontColor : '#333' ,
separator : { show : true , color : '#ccc' , width : 1 }
}
Provides labels and categorical divisions.
{
type : 'header' ,
headerText : 'Quarter Goals' ,
active : true ,
cells : 4 , // Number of divisions
color : '#ffffff' ,
height : 12 ,
fontSize : 9 ,
fontColor : '#666'
}
Shows events positioned by their dates.
{
type : 'data' ,
name : 'Events' , // Ring identifier for setData()
active : true ,
color : '#4ECDC4' ,
unit : 'day' , // Options: 'day', 'week', 'month', 'quarter'
height : 20 ,
fontSize : 10 ,
fontColor : '#fff'
}
Events must include date information in ISO format (YYYY-MM-DD):
{
label : 'Event Name' , // Required: Display text
startDate : '2025-03-15' , // Required: ISO date string
endDate : '2025-03-15' , // Required: ISO date string
color : '#FF6B6B' , // Optional: Override ring color
description : 'Details...' // Optional: Shown in detail panel
}
new CircularTimeline ( container , config , callbacks )
Parameters:
container: CSS selector string or DOM element
config: Configuration object (see Configuration section)
callbacks: Optional object with interaction handlers
Callbacks:
{
onSegmentClick : ( event ) => { /* ... */ } ,
onSegmentHover : ( event ) => { /* ... */ } ,
onSegmentLeave : ( event ) => { /* ... */ }
}
setData(events, ringName)
Add or update events for a specific data ring.
timeline . setData ( [
{ label : 'Event 1' , startDate : '2025-01-15' , endDate : '2025-01-15' } ,
{ label : 'Event 2' , startDate : '2025-02-01' , endDate : '2025-02-28' }
] , 'Events' ) ;
Get all rings in the visualization.
const rings = timeline . getRings ( ) ;
Clean up and remove the visualization.
Check out the live demo on CodePen or browse the examples directory .
To run examples locally:
npx serve .
# Then open http://localhost:3000/examples/demo.html
Great for:
Annual planning and goal tracking
Project timelines that repeat yearly
Seasonal business processes
Multi-year comparisons
Educational calendars
Not ideal for:
Long linear sequences (use traditional timelines)
Non-cyclical data
Very short time periods (days/weeks only)
Works in all modern browsers that support ES6 modules and SVG:
Chrome/Edge 61+
Firefox 60+
Safari 11+
Opera 48+
No build step required for development.
This software is dual-licensed:
For open source projects and non-commercial use:
Free under AGPL v3.0 license
See LICENSE file for full terms
For commercial/closed-source use:
Commercial license required
Open an issue or email: [[email protected] ]
We're happy to discuss licensing options!
Not sure which applies? Feel free to ask!
Issues and pull requests welcome! Visit the GitHub repository to contribute.
Fixed year boundary separator rendering issue
Improved week segment calculations to eliminate visual artifacts
Converted to ES6 modules
Simplified usage and examples
See full changelog for more details.