I'm trying to create a horizontal timeline with lots of simultaneous events. Think of something like the American presidents, British monarchs, Catholic popes, and significant world events. I've played around with chronology.sty, which wasn't flexible enough, and chronosys.sty, which got me closer, but ideally I'm looking for something similar to the screenshot below, which I found just googling around for timelines. Can anyone suggest a package that might be suitable? The labeling displayed in the image is particularly interesting to me.
-
1Welcome to TeX.SX! From what I show, it seems not to be too complicated to draw this from scratch using a few custom macros. Does the height of the text have some meaning? Or is the height just used to avoid overlapping?Jasper Habicht– Jasper Habicht2025-06-25 17:59:35 +00:00Commented Jun 25 at 17:59
-
For my purposes, the height of the different entries does not signify anything, although it's convenient for grouping (all the presidents on top, all the monarchs in the middle, all the popes at the bottom, for example) and for avoiding overlapping.novelgazer– novelgazer2025-06-25 18:24:14 +00:00Commented Jun 25 at 18:24
-
1ctan.org/pkg/pgfganttsamcarter_is_at_topanswers.xyz– samcarter_is_at_topanswers.xyz2025-06-25 18:38:39 +00:00Commented Jun 25 at 18:38
-
See ctan.org/pkg/pgfgantt, and topic Gantt mentioned there, too.MS-SPO– MS-SPO2025-06-25 18:39:05 +00:00Commented Jun 25 at 18:39
-
2You may also want to have a look at the chronos package ctan.org/pkg/chronosMS-SPO– MS-SPO2025-06-25 19:06:28 +00:00Commented Jun 25 at 19:06
|
Show 2 more comments
2 Answers
You could create your own l3draw-based environment:
\DocumentMetadata{}
\documentclass[border=10pt]{standalone}
\usepackage{l3draw}
\ExplSyntaxOn
\dim_new:N \l_novelgazer_timeline_x_dim
\dim_new:N \l_novelgazer_timeline_y_dim
\int_new:N \l_novelgazer_timeline_year_step_int
\coffin_new:N \l_novelgazer_timeline_year_coffin
\coffin_new:N \l_novelgazer_timeline_event_desc_coffin
\coffin_new:N \l_novelgazer_timeline_event_time_coffin
\dim_set:Nn \l_novelgazer_timeline_x_dim { 2.5pt }
\dim_set:Nn \l_novelgazer_timeline_y_dim { 1cm }
\int_set:Nn \l_novelgazer_timeline_year_step_int { 25 }
\cs_new_protected:Npn \novelgazer_timeline_year_span:nn #1#2 {
\tl_if_empty:nTF {#2} {
#1
} {
#1 -- #2
}
}
\cs_new_protected:Npn \novelgazer_timeline_event:nnnnn #1#2#3#4#5 {
\draw_scope_begin:
\color_select:n {#1}
\opacity_select:n { 0.5 }
\draw_set_linewidth:n { 0.4pt }
\draw_path_moveto:n {
#4 * \l_novelgazer_timeline_x_dim ,
0pt
}
\draw_path_lineto:n {
#4 * \l_novelgazer_timeline_x_dim ,
#3 * \l_novelgazer_timeline_y_dim +
\tl_if_empty:nTF {#5} { 0.75em } { 0pt }
}
\draw_path_use_clear:n { stroke }
\opacity_select:n { 1 }
\hcoffin_set:Nn \l_novelgazer_timeline_event_desc_coffin {
\textbf{#2}
}
\draw_coffin_use:Nnnn \l_novelgazer_timeline_event_desc_coffin
{ l } { b } {
#4 * \l_novelgazer_timeline_x_dim + 5pt ,
#3 * \l_novelgazer_timeline_y_dim +
\tl_if_empty:nTF {#5} { 2.5pt } { 5pt }
}
\hcoffin_set:Nn \l_novelgazer_timeline_event_time_coffin {
\novelgazer_timeline_year_span:nn {#4} {#5}
}
\draw_coffin_use:Nnnn \l_novelgazer_timeline_event_time_coffin
{ l } { t } {
#4 * \l_novelgazer_timeline_x_dim + 5pt ,
#3 * \l_novelgazer_timeline_y_dim -
\tl_if_empty:nTF {#5} { 2.5pt } { 5pt }
}
\tl_if_empty:nF {#5} {
\draw_set_linewidth:n { 0.8pt }
\draw_path_moveto:n {
#4 * \l_novelgazer_timeline_x_dim - 0.2pt ,
#3 * \l_novelgazer_timeline_y_dim
}
\draw_path_lineto:n {
#5 * \l_novelgazer_timeline_x_dim ,
#3 * \l_novelgazer_timeline_y_dim
}
\draw_path_use_clear:n { stroke }
}
\draw_scope_end:
}
\NewDocumentCommand { \event } { O{blue} m O{1} m m } {
% color
% description
% vertical shift
% start year
% end year
\novelgazer_timeline_event:nnnnn
{#1} {#2} {#3} {#4} {#5}
}
\NewDocumentEnvironment { timeline } { m m +b } {
% start year
% end year
\draw_begin:
\draw_path_moveto:n {
#1 * \l_novelgazer_timeline_x_dim - 5pt ,
0pt
}
\draw_path_lineto:n {
#2 * \l_novelgazer_timeline_x_dim + 5pt ,
0pt
}
\draw_path_use_clear:n { stroke }
\int_step_inline:nnnn
{#1}
{ \l_novelgazer_timeline_year_step_int }
{#2} {
\draw_path_moveto:n {
##1 * \l_novelgazer_timeline_x_dim ,
0pt
}
\draw_path_lineto:n {
##1 * \l_novelgazer_timeline_x_dim ,
-5pt
}
\draw_path_use_clear:n { stroke }
\hcoffin_set:Nn \l_novelgazer_timeline_year_coffin {
##1
}
\draw_coffin_use:Nnnn \l_novelgazer_timeline_year_coffin
{ hc } { t } {
##1 * \l_novelgazer_timeline_x_dim , -10pt
}
}
#3
\draw_end:
} { }
\ExplSyntaxOff
\begin{document}
\begin{timeline}{1600}{1750}
\event[green!75!black]{Petition of Right}{1625}{}
\event{John Locke}[2]{1632}{1704}
\end{timeline}
\end{document}
-
This looks really promising, thank you. I'll play around with it and report back to the thread.novelgazer– novelgazer2025-06-25 21:33:55 +00:00Commented Jun 25 at 21:33
-
Having some trouble getting this to produce output. Getting the following error:
! Undefined control sequence. \novelgazer_timeline_event:nnnnn ...city_select:n {0.5}\draw_set_linewidth:n... l.162 \end {timeline}novelgazer– novelgazer2025-06-28 00:59:59 +00:00Commented Jun 28 at 0:59 -
@novelgazer Try loading
l3opacitypackage or update to the newest kernel.Jasper Habicht– Jasper Habicht2025-06-28 06:10:39 +00:00Commented Jun 28 at 6:10
This requires version 0.9.3 or later of chronos.
I'm not entirely satisfied with the style, so that isn't included yet, but the line add yshift which enables lines at varying heights to represent periods of time, is.
A possible style utilising this feature might be shifted lines:
% ateb: https://tex.stackexchange.com/a/748366/
\documentclass{standalone}
\usepackage{chronos}
\makeatletter
\chronosset{%
shifted lines/.style={
/utils/exec={\selectcolormodel{rgb}},
/chronos/.cd,
shifted lines/.meaning to context,
colour scheme=xcolseries,
rotate all colours,
timeline={%
timeline height=1pt,
major step font=\footnotesize,
minor years=false,
timeline width=150mm,
step major years=50,
timeline years=off line,
timeline years=below,
timeline mark={chronos timeline foreground colour,thin},
timeline year={text=chronos timeline foreground colour,fill=none},
timeline years anchor=north,
},
every event above,
every period above,
every life above,
only years,
without eras,
no connectors,
every connections+={%
draw=####1,
-,
},
every text tags+={text=####1!75!black,font=\footnotesize,align=left},
period/tag+={at aux=start,tag anchor=south west,},
life/tag+={at aux=birth,tag anchor=south west,},
period/connection+={shorten <=-\chronos@llinell@yshift@base},
life/connection+={shorten <=-\chronos@llinell@yshift@base},
event/line'={draw=none},
line yshift=5pt,
event/tag+={tag anchor=north west,anchor=south west},
connections on=background,
},
}
\makeatother
\begin{document}
\begin{chronos}
[
shifted lines,
timeline={
start date=1600-01-01,
end date=1850-01-01,
},
]
\chronoslife{dates=1632:1704,name=john locke,line add yshift=25pt}
\chronoslife{dates=1685:1753,name=george berkeley,}
\chronoslife{dates=1711:1776,name=david hume,line add yshift=25pt}
\chronosperiod{dates={1765}:{1783},name=american revolution,line add yshift=130pt,}
\chronosevent{date={1776},name=declaration of independence,yshift=110pt}
\chronosevent{date={1776},name=\emph{common sense},yshift=90pt}
\chronosevent{date={1791},name=\emph{rights of man},yshift=70pt}
\chronosperiod{dates={1789}:{1799},name=french revolution,line add yshift=45pt}
\chronosevent{date={1793},name=\emph{the age of reason},yshift=25pt}
\chronosevent{date={1795},name=\emph{agrarian justice},yshift=0pt}
\end{chronos}
\end{document}
-
probably the connection point should be variable, at least for lives/periods?cfr– cfr2025-07-22 01:41:14 +00:00Commented Jul 22 at 1:41


