diff --git a/docs/conf.py b/docs/conf.py
index 86042bb36464c6b2dbe14f5df515c44b902e6141..dc7d748bd74d37f351df59e8b30c057d164734d0 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -122,6 +122,9 @@ autodoc_typehints = "description"
 pyvista.BUILDING_GALLERY = True
 pyvista.OFF_SCREEN = True
 
+# Disable progress bars in sphinx
+os.environ["TQDM_DISABLE"] = "1"
+
 sphinx_gallery_conf = {
     "examples_dirs": ["examples"],
     "gallery_dirs": ["auto_examples"],
diff --git a/docs/examples/howto_meshplotlib/plot_animation.py b/docs/examples/howto_meshplotlib/plot_animation.py
index c77fb637a25d478a4f680589107e33adc46412bc..8e5ab568dabd5612c9de9da98bc6e5580a8542d0 100644
--- a/docs/examples/howto_meshplotlib/plot_animation.py
+++ b/docs/examples/howto_meshplotlib/plot_animation.py
@@ -24,6 +24,7 @@ mesh_series = examples.meshseries_CT_2D
 # Let's use fixed scale limits to prevent rescaling during the animation.
 setup.p_min = 0
 setup.p_max = 100
+setup.dpi = 50
 
 # %%
 # You can choose which timesteps to render by passing either an int array
diff --git a/docs/user-guide/third-party.md b/docs/user-guide/third-party.md
new file mode 100644
index 0000000000000000000000000000000000000000..748e5958a0de6dd908d7e627e44bb2aa296a3bc1
--- /dev/null
+++ b/docs/user-guide/third-party.md
@@ -0,0 +1,13 @@
+# Third-party applications
+
+```{eval-rst}
+.. sectionauthor:: Florian Zill (Helmholtz Centre for Environmental Research GmbH - UFZ)
+```
+
+`ogstools` makes use of some several third party python packages:
+
+- [tqdm](https://github.com/tqdm/tqdm): progress bars in computionally
+  expensive loops
+  - if you want to globally deactivate them please set the following environt
+    variable: `os.environ["TQDM_DISABLE"] = "1"`
+- ...
diff --git a/ogstools/meshplotlib/animation.py b/ogstools/meshplotlib/animation.py
index dfc89fd1fa6d97b91aca1717d97c32cc1d50dbf3..cf58f2e7891a495e32ae0a4abf788561d14dc018 100644
--- a/ogstools/meshplotlib/animation.py
+++ b/ogstools/meshplotlib/animation.py
@@ -9,25 +9,13 @@ import matplotlib as mpl
 import numpy as np
 from matplotlib import figure as mfigure
 from matplotlib.animation import FFMpegWriter, FuncAnimation, ImageMagickWriter
+from tqdm.auto import tqdm
 
 from ogstools.meshlib import MeshSeries
 from ogstools.propertylib import Property
 
 from . import setup
-from .core import _plot_on_figure, plot, plt
-
-
-def timeline(ax: plt.Axes, x: float, xticks: list[float]) -> None:
-    y = 1.1
-    ap = {"arrowstyle": "-", "color": "k"}
-    axfr = "axes fraction"
-    ax.annotate("", (1, y), (0, y), axfr, arrowprops=ap)
-    align = dict(ha="center", va="center")  # noqa: C408: noqa
-    for xt in xticks:
-        ax.annotate("|", (xt, y), (xt, y), axfr, **align, size=28)
-    align = dict(ha="center", va="center")  # noqa: C408: noqa
-    style = dict(color="g", size=36, weight="bold")  # noqa: C408: noqa
-    ax.annotate("|", (x, y), (x, y), axfr, **align, **style)
+from .core import _plot_on_figure, plot
 
 
 def animate(
@@ -48,12 +36,7 @@ def animate(
     setup.layout = "tight"
 
     ts = mesh_series.timesteps if timesteps is None else timesteps
-    tv = mesh_series.timevalues
 
-    def t_frac(t):
-        return (t - tv[0]) / (tv[-1] - tv[0])
-
-    xticks = [t_frac(t) for t in tv]
     fig = plot(mesh_series.read(0, False), property)
 
     def init() -> None:
@@ -61,10 +44,6 @@ def animate(
 
     def animate_func(i: Union[int, float], fig: mfigure.Figure) -> None:
         index = np.argmin(np.abs(np.asarray(ts) - i))
-        progress = 100 * index / (len(ts) - 1)
-        bar = "â–ˆ" * int(progress) + "-" * (100 - int(progress))
-        end = "\n" if index == (len(ts) - 1) else "\r"
-        print(f"progress: |{bar}| {progress:.2f}% complete", end=end)
 
         fig.axes[-1].remove()  # remove colorbar
         for ax in np.ravel(fig.axes):
@@ -78,12 +57,17 @@ def animate(
         with warnings.catch_warnings():
             warnings.simplefilter("ignore")
             fig = _plot_on_figure(fig, mesh, property)
-            x = i / len(ts) if isinstance(i, int) else t_frac(i)
-            timeline(fig.axes[0], x, xticks)
 
     _func = partial(animate_func, fig=fig)
+
     return FuncAnimation(
-        fig, _func, ts, blit=False, interval=50, repeat=False, init_func=init
+        fig,
+        _func,
+        frames=tqdm(ts),
+        blit=False,
+        interval=50,
+        repeat=False,
+        init_func=init,
     )
 
 
@@ -99,7 +83,7 @@ def save_animation(anim: FuncAnimation, filename: str, fps: int) -> bool:
     print("Start saving animation...")
     codec_args = (
         "-crf 28 -preset ultrafast -pix_fmt yuv420p "
-        "-vf crop='iw-mod(iw,2)':'ih-mod(ih,2)'"
+        "-vf pad=ceil(iw/2)*2:ceil(ih/2)*2"
     ).split(" ")
 
     writer = None
diff --git a/ogstools/studies/convergence/convergence.py b/ogstools/studies/convergence/convergence.py
index 151ce2cb1c2dab5ed54c43ffc66f497d5e3c9932..aa888e756a9de8251734523b3ff542300b4cb349 100644
--- a/ogstools/studies/convergence/convergence.py
+++ b/ogstools/studies/convergence/convergence.py
@@ -5,6 +5,7 @@ import numpy as np
 import pandas as pd
 import pyvista as pv
 from pint import UnitRegistry
+from tqdm.auto import tqdm
 
 from ogstools import meshlib, propertylib
 
@@ -270,7 +271,7 @@ def convergence_metrics_evolution(
     p_metrics_per_t = np.empty((0, 6))
 
     timestep_sizes = [np.mean(np.diff(ms.timevalues)) for ms in mesh_series]
-    for timevalue in common_timevalues:
+    for timevalue in tqdm(common_timevalues):
         meshes = [ms.read_closest(timevalue) for ms in mesh_series]
         reference = richardson_extrapolation(
             meshes, property, meshes[-3], refinement_ratio
diff --git a/pyproject.toml b/pyproject.toml
index 3894751266e6cd4ccc1405d5be49707aa5fc13b8..d8271e53ec9ffd9751caddc3700c75dbccb9b15c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,6 +25,7 @@ dependencies = [
   "papermill>=2.4.0",
   "ogs>=6.5.0",
   "ogs6py>=0.370",
+  "tqdm>=4.60",
 ]
 
 [project.urls]