ProgressMeter.jl gives Julia loops a progress bar, an ETA and three kinds of counter
Progress meter for long-running computations
At a glance
- What is it?
- ProgressMeter.jl is a small MIT licensed Julia package for reporting on long running computations, with a macro for known length loops, threshold and unknown length counters, and thread safe updates. Its interesting edges are the update interval, the byte based manual counter and the fact that an unknown length run gives you a count but no ETA.
- Who is it for?
- Use ProgressMeter.jl when a Julia job runs long enough that silence looks like a hang, and reach for @showprogress first because it needs one line, then switch to ProgressThresh or ProgressUnknown when the stopping rule is a tolerance or a nondeterministic condition. Do not expect it to tell you how far along a task with no known total is, since ProgressUnknown only counts calls to next!, and do not trust its ETA on a job whose iterations vary wildly in cost.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Julia, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A progress bar for Julia loops that know their length
A Julia script that runs for twenty minutes without writing a line looks exactly like a script that crashed. ProgressMeter.jl exists for that gap. It is a package for reporting on long running operations, and its core idea is small: give it a total, tell it when work completes, and it draws a bar, a percentage and an estimated time remaining.
The intended user is a Julia developer running a loop, comprehension, map, pmap or reduce over a collection whose length is known, on a loop where each iteration takes long enough that silence is confusing. The bar is not a separate service and it does not watch your process. It is code you call at the point where progress happens, either through a macro or through two functions.
The style follows the familiar Python progressbar look, with a filled glyph run, a percentage, a label and an ETA field, and the README states that the output is similar to python-progressbar. What is specific to Julia is the type system around the counter. ProgressMeter.jl gives you a counter type that matches your stopping rule instead of one universal counter with a guessed total, and it makes that counter safe to update from several threads.
Installing ProgressMeter.jl and running the first loop
Installation happens inside Julia, by adding the package to the current environment:
using Pkg; Pkg.add("ProgressMeter")With the package loaded, the shortest useful example is the @showprogress macro wrapped around a for loop:
using Distributed
using ProgressMeter
@showprogress dt=1 desc="Computing..." for i in 1:50
sleep(0.1)
endYou should see a bar labelled Computing... with a percentage, a filled and empty glyph run and an ETA, and when the loop ends the final duration. The dt=1 argument sets a minimum update interval of one second, so the bar redraws at most once per second no matter how fast the loop runs. That interval is the mechanism that keeps the meter cheap: if your computation finishes so quickly that it never needs to show progress, no extraneous output appears at all. Set dt to a smaller value when you want a more responsive bar on a slower job.
The macro is not limited to plain for loops. It also wraps a comprehension, a @distributed for loop, and map, pmap or reduce, as long as the object being iterated over implements the length method and handles continue correctly. That length requirement is the part worth remembering, because it is the one case where the macro silently cannot tell you the total.
Three counter types for three kinds of stopping rule
The package ships three counters and the right one depends on when your job stops. Progress(n) is the familiar one, a job with a known number of steps. ProgressThresh is for jobs that terminate when a criterion is met, such as an optimisation that converges inside a tolerance:
prog = ProgressThresh(1e-5; desc="Minimizing:")
for val in exp10.(range(2, stop=-6, length=20))
update!(prog, val)
sleep(0.1)
endHere you push a measured value in with update! and the meter decides whether the criterion has been reached. ProgressUnknown covers the case where the number of steps is not known in advance, because the job stops on a nondeterministic condition. It counts calls to next! and stops counting when you call finish!:
prog = ProgressUnknown(desc="Titles read:")
for val in ["a" , "b", "c", "d"]
next!(prog)
if val == "c"
finish!(prog)
break
end
sleep(0.1)
endTwo details are worth flagging. The count for ProgressUnknown is the number of next! calls, so if the unit of work is not one iteration you have to set the counter by hand with update!. And by default finish! swaps the spinner for a success mark, which you can override with the spinner keyword, passing a different character when the job failed rather than succeeded. There is also a spinner=true mode that shows an animated symbol while the job runs, and you can hand it a string of characters if you want to drive the animation yourself.
When the unit is bytes rather than iterations
Some jobs have a known total but no meaningful iteration count, which is where driving the counter yourself pays off. The documentation's own example reads a file and reports how many bytes have been consumed:
function readFileLines(fileName::String)
file = open(fileName,"r")
seekend(file)
fileSize = position(file)
seekstart(file)
p = Progress(fileSize; dt=1.0) # minimum update interval: 1 second
while !eof(file)
line = readline(file)
# Here's where you do all the hard, slow work
update!(p, position(file))
end
endThe total comes from seeking to the end of the file and asking for position, and each update! reports the current position, so the percentage tracks bytes rather than lines. That is a real difference from the macro version, which reports iterations, and it matters when the work per unit is uneven. A line count would move in visible jumps for a file with long lines, while bytes give a smoother curve. The flip side is that byte progress says nothing about how much work remains, since the cost of parsing a line is not a function of its length.
The same manual approach works for any known total. Progress(n; dt=1.0) followed by next!(p) in the body is the smallest form of it, and it is the form to use when the loop body has branches that make a macro wrapper awkward.
Threads, @distributed and pmap
Parallel work is where progress meters usually break, because several workers advance one counter at once. The core methods Progress(), ProgressThresh(), ProgressUnknown() and their updaters are documented as thread safe, so they can be used with Threads.@threads and Threads.@spawn:
using ProgressMeter
p = Progress(10)
Threads.@threads for i in 1:10
sleep(2*rand())
next!(p)
end
finish!(p)Notice that finish! is called explicitly here. With the macro form the completion is handled for you, but in the manual form nothing marks the bar as done until you say so.
Distributed support is a separate mechanism. The README states that support for @distributed and pmap is a package extension that loads with using Distributed, which is why the first example imports Distributed before ProgressMeter. The extension is visible in the repository layout, where an ext/ directory sits alongside src/ and test/.
For a @distributed for loop without a reducer, an @sync is implied, so you do not need to write one. A reducer form assigns the result through the macro, as in result = @showprogress desc="Computing..." @distributed (+) for i in 1:10. The practical consequence is that enabling Distributed pulls in extra code paths, and the bar for a pmap job reports on completed items rather than on the internal state of each worker.
Drawing the bar: desc, barglyphs, barlen and color
The rendering is configurable through keyword arguments on the constructor. A description string is prepended to the output, and the bar length is measured in characters. BarGlyphs sets the characters used for the bar and can be built from five separate characters or a single five character string:
p = Progress(n; dt=0.5, barglyphs=BarGlyphs("[=> ]"), barlen=50, color=:yellow)That produces a bar of fifty characters using the arrow and bracket glyphs, drawn in yellow. A vector of characters can be given as a transition between the empty glyph and the filled glyph, so the bar fills with intermediate steps rather than snapping from blank to solid. That second form takes a set of ramp characters between the two end glyphs, and it makes the bar read better on a narrow terminal than a hard jump. These are cosmetic settings, but they are also the reason the bar survives a real terminal: bar length and glyph set are yours to pick, and dt stays independent of how the bar looks.
Where the meter stops being informative
Several boundaries are worth stating plainly. An unknown length job gives you a count of next! calls and nothing else: with ProgressUnknown there is no total, so there is no percentage and no ETA. The documentation does not describe any way to estimate a remaining time for that case.
The ETA on a known length job is an average of observed rates, so it is only as honest as the uniformity of the work. A loop whose first thousand iterations are cheap and whose last ten are expensive will report an ETA that keeps slipping, because the meter has no way to know what is coming. The same applies to the file reading example, where bytes consumed is not work done.
The macro has a hard requirement on the iterated object: it must implement length and handle continue correctly. Without length there is no total and the macro has nothing to divide by. And the repository is a package rather than a framework, so it offers no persistence: a job that is interrupted loses its progress history, and there is no resume file to reload. The README also stops short of describing how to redirect the output, how the bar behaves when stdout is not a terminal, or how to integrate with a logging system, so those cases need your own code.
MIT licence, a recent push and a slow release cadence
The project is MIT licensed, which permits internal and commercial use and modification, with the condition that the copyright notice and licence text accompany copies of the code. The repository is not archived and the last push was on 2026-10-19, so the source tree is moving.
Releases move separately and more slowly. The recorded versions are v1.11.0 from 2025-08-24, then v1.10.4 and v1.10.3 both dated 2025-04-04, with the patch releases on the same day. A package this small is unlikely to need a long upgrade conversation, but note that the tagged release and the repository head are not the same thing, and the tree also carries a benchmark/ directory alongside src/, test/, ext/, .github/, Project.toml, LICENSE.txt and README.md.
The practical upgrade path for a user is the standard one for a Julia package: add or update it in your environment and let the package manager resolve the version. What the repository does not offer is a changelog entry that tells you what changed between tags, so check the diff for the version you move to if you depend on the exact rendering of a bar.
Editorial conclusion
Use ProgressMeter.jl when a Julia job runs long enough that silence looks like a hang, and reach for @showprogress first because it needs one line, then switch to ProgressThresh or ProgressUnknown when the stopping rule is a tolerance or a nondeterministic condition. Do not expect it to tell you how far along a task with no known total is, since ProgressUnknown only counts calls to next!, and do not trust its ETA on a job whose iterations vary wildly in cost. Before you adopt it, check that your iterator implements length, because @showprogress depends on it, and read how dt interacts with your update rate so the bar redraws often enough to be useful and rarely enough to stay cheap.
Frequently asked questions
How do I install ProgressMeter.jl?
Add it to your Julia environment with the package manager, using using Pkg; Pkg.add("ProgressMeter"). The package requires no other setup beyond importing it.
Does ProgressMeter.jl work with parallel and threaded Julia code?
Yes. The core methods Progress(), ProgressThresh(), ProgressUnknown() and their updaters are thread safe, and support for @distributed and pmap ships as a package extension that loads with using Distributed.
How do I show progress when I do not know the number of steps?
Use ProgressUnknown and call next! as work completes, then finish! when the job ends. It displays the number of next! calls, so there is no percentage or ETA for that case, and you can set the counter by hand with update! if your unit is not one iteration.
What does the dt keyword control in ProgressMeter.jl?
It sets the minimum interval between progress redraws, so a bar created with dt=1.0 updates at most once per second. A job that finishes before the first update produces no progress output at all.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/timholy-progressmeter-jl)Was this useful?