Quick Reference · experiment visualization in Python · TensorBoard 2.21 (Jun 2026)

tensorboard tf.summary · Keras · PyTorch

Nothing in TensorBoard is live. Your code appends records to event files in a log directory; TensorBoard is a web server that polls that directory and redraws whatever it finds. The blank dashboard, the duplicated run, the plot that stopped updating — every one of them is a question about those files, not about the UI.
install & launch log dir & runs tf.summary framework integration dashboards & UI plugins & tooling gotcha / removed most common
Cross-checked against: tensorflow.org/tensorboard (guide + API) · github.com/tensorflow/tensorboard (RELEASE.md, flags) · pytorch.org/docs (torch.utils.tensorboard) · openxla.org/xprof · pypi.org (tensorboard 2.21.0, Jun 2026). Verified 2026-08-27 against TensorBoard 2.21.0.
Where the numbers actually go
A · ONE DIRECTION ONLY — NOTHING IS PUSHED BACK your training code tf.summary · Keras callback torch SummaryWriter write buffer max_queue · flush_millis unflushed = invisible flush event files on disk events.out.tfevents.… append-only, per run poll tensorboard --logdir logs reload every 5s browser localhost:6006 B · ONE SUBDIRECTORY = ONE RUN = ONE LINE ON THE CHART logs/ └─ fit/ ├─ 20260719-093000/ ├─ train/    ← run "fit/20260719-093000/train" └─ validation/ ← a second, separate run └─ 20260719-101500/ ← yesterday's attempt, still shown --logdir logs point at the PARENT, not at a run the run name is the relative path so directory naming is chart naming — design it two writers into one directory interleaves the steps and zigzags the line C · THREE WRITERS, ONE FILE FORMAT — AND ONE THAT LEFT tf.summary the native API keras.callbacks TensorBoard(log_dir=...) SummaryWriter torch.utils.tensorboard the same event file TensorBoard needs no TensorFlow the Profile tab tensorboard_plugin_profile → renamed XProf moved to OpenXLA · pip install xprof JAX · PyTorch/XLA · TF · runs standalone left
# --- write (TensorFlow / Keras) -------------------------------
import tensorflow as tf, keras, datetime
logdir = "logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
cb = keras.callbacks.TensorBoard(log_dir=logdir, histogram_freq=1)
model.fit(ds, epochs=10, validation_data=val, callbacks=[cb])

# --- write (PyTorch) ------------------------------------------
from torch.utils.tensorboard import SummaryWriter
w = SummaryWriter("runs/exp1")
w.add_scalar("loss/train", loss.item(), global_step)   # step is required
w.close()                                              # or nothing appears

# --- read -----------------------------------------------------
tensorboard --logdir logs --port 6006          # parent dir, not a run
Write, flush, point at the parent directory. The rest of this sheet is detail.
PART I

the tool  ·  launching and reading

TensorBoard is a reader. It has no opinion about how the files got there and no dependency on TensorFlow — pip install tensorboard alone is enough to visualise runs written by PyTorch, JAX or anything else that emits the event format. Since TF 2.21 it is no longer installed with TensorFlow.
01Install & launchsixty seconds
02The log directorythe only real concept
03Flags that matterthe CLI
04Notebooks & Colabinline
05Remote & sharedoff your laptop
06The dashboardswhat each tab is
07Reading a chartthe controls
08Comparing runsthe actual job
09Graphsthe model, drawn
10Images, audio, textnon-scalar logging
11Histograms & distributionswatching the weights
12Housekeepingdisk and staleness
PART II

writing events  ·  from your code

Three APIs, one file format. tf.summary is the native one, the Keras callback wraps it, and PyTorch ships its own writer producing identical files. Whichever you use, the same two rules apply: every record needs an explicit step, and nothing is visible until it is flushed.
W1tf.summary · the writeropen, write, flush
W2tf.summary · what you can logthe record types
W3Steps & conditional loggingcontrolling volume
W4The Keras callbackthe one-liner
W5Custom loopsdoing it by hand
W6PyTorch · SummaryWriterthe other native writer
W7PyTorch · the rest of add_*beyond scalars
W8Other frameworksit is just event files
PART III

plugins  ·  past the loss curve

The dashboards that need their own API, plus the two things worth knowing about the ecosystem: the profiler has lefttensorboard_plugin_profile is now XProf under OpenXLA, installable and runnable on its own — and TensorBoard.dev has been shut down, so sharing runs means self-hosting or a third-party service.
P1HParamswhich settings won
P2Embedding Projectorhigh-dimensional space
P3PR curves & custom scalarsthe quieter plugins
P4XProf · the profilerit left TensorBoard
P5Reading the data backout of the UI
P6The remaining tabsMesh, Debugger, What-If
P7What is gone, and what elsethe honest ending

Four pictures worth the whole reference

Where runs come from, the three x-axes, what smoothing really does, and why the dashboard is empty.

1 · directories become runs

TensorBoard walks the tree below --logdir. Every directory containing event files becomes one run, named by its path relative to the root — which is why folder naming is chart naming.

on disk logs/ └─ fit/ ├─ base/ ├─ train/ └─ validation/ └─ tuned/ ├─ train/ └─ validation/ scan run selector fit/base/train fit/base/validation fit/tuned/train fit/tuned/validation four runs, four colours, one chart

2 · the three x-axes

The same two runs, plotted three ways. Step compares learning; relative compares speed; wall reveals the gap where the job was queued.

STEP both end at step 1000 compare learning RELATIVE teal took longer compare speed WALL queued real clock time find the stall Step is the default, and the right one almost always. Relative answers “which configuration trains faster in wall-clock terms”. Wall answers “where did the afternoon go” — queueing, checkpointing, a stalled loader. Two runs logging at different step semantics cannot be compared on any of them.

3 · what smoothing does

The slider applies an exponential moving average and draws the raw series faintly behind it. It is a reading aid, not data — and at both ends it is wrong.

biased start lags the end faint = the values you logged · bold = the moving average smoothing 0 shows the truth. 0.6 is a readable default. 0.99 can invent a trend. Never quote a final metric off the bold line — read it from the raw series or your logs.

4 · why the dashboard is empty

Records sit in a buffer until max_queue fills, flush_millis elapses, or you flush. A short script can finish, exit, and leave everything unwritten.

NO FLUSH add_scalar × 500 buffer (in RAM) process exits buffer discarded 0 bytes FLUSHED add_scalar × 500 buffer (in RAM) w.close() or w.flush() event file Default flush_millis is 120000 — two minutes. Shorter scripts never reach it. Fixes: close the writer, use a with-block, flush each epoch, or lower flush_millis.

Worth memorizing

one directory = one runand the run name is its path relative to --logdir
--logdir is the parentpoint it at a single run and you lose every comparison
step is requiredevery summary call takes an explicit step — there is no implicit counter
flush or it never existeddefault flush_millis is two minutes; short scripts exit first
close the writeror use a with-block — the commonest cause of a missing last epoch
no TensorFlow neededpip install tensorboard alone reads PyTorch and JAX runs
not bundled with TF anymoresince TF 2.21 you install TensorBoard yourself
slashes group tagsloss/train and loss/val collapse into one loss section
charts are downsampled~1000 points per tag; raise it with --samples_per_plugin
smoothing is an EMAa reading aid that is biased at the start and lags at the end
three x-axesstep for learning, relative for speed, wall for stalls
PyTorch images are CHWadd_image CHW, add_images NCHW — wrong layout fails silently
histogram_freq defaults to 0so the Histograms and Distributions tabs stay empty until you set it
fresh timestamped dir per runre-running into the same one makes the step axis zigzag backwards
reload_interval is 5sa stale chart is usually that, the flush, or the browser cache
missing tab = missing datacheck the inactive-plugin dropdown before suspecting the install
no authentication at allwhoever reaches the port sees everything — think before --bind_all
the profiler is now XProfpip install xprof; moved to OpenXLA, covers JAX and PyTorch/XLA too
TensorBoard.dev is deadthe upload subcommand errors immediately; sharing means self-hosting
read files, not the UItbparse or EventAccumulator (call Reload()) for the full series