How to Visualize BAM Alignment Files in the UCSC Genome Browser
By Dr. Zubair Khalid, DVM, MS, PhD ·

Most failed BAM tracks on the UCSC Genome Browser fail for one boring reason: the browser cannot find an index file sitting next to the BAM, or it cannot reach the BAM at a public URL at all. The first thing to check is not the browser settings. Open the exact BAM URL in a new tab, confirm the file downloads, then change the last characters to .bai and confirm that URL also returns a file. If either check fails, no amount of track line editing will help.
The UCSC Genome Browser has displayed user-supplied BAM data as custom tracks since its 2011 release, and the browser reads alignment chunks on demand rather than downloading the whole file [1]. That design is why the index and the hosting details matter so much. The browser asks a web server for a byte range covering the region you are looking at. If the server cannot answer that request, the track stays blank even though the BAM is perfectly valid. The same architecture explains why the BAM must match the assembly and the chromosome names of the reference genome you have selected on screen.
This guide works through the mechanism of each failure, gives the exact commands that confirm or rule out each cause, and ends with practices that keep future tracks working. It also places the BAM in context among the common bioinformatics file formats you will handle on the way to a finished figure.
What UCSC Actually Needs From a BAM File
A BAM file is the binary, compressed form of the Sequence Alignment/Map format. Each record describes one sequenced read placed at a genomic coordinate, with a flag, a mapping quality, a CIGAR string that encodes insertions and deletions, the read sequence, and per-base quality scores. The binary layout is what makes random access possible, and random access is the entire point of a genome browser track.
The four hard requirements
Every BAM custom track needs four things to be true at the same time.
- The BAM must be reachable at a stable HTTP or HTTPS URL. It cannot live only on your laptop.
- A BAM index must sit beside it, and the browser must be able to guess its location from the BAM URL.
- The records must be coordinate sorted, so that reads for a region are stored in a contiguous block the browser can fetch.
- The reference sequence names and lengths embedded in the BAM header must match the assembly currently displayed in the browser.
Break any one of these and the symptom looks similar, which is why diagnosis is faster when you test each requirement separately instead of changing several things at once.
The browser's custom track system is the mechanism for bringing private data alongside the curated annotation tracks, and the same system supports BAM, VCF, bigWig, and bigBed inputs [2]. The BAM support was added specifically so that high-throughput sequencing alignments could be inspected at single-base resolution next to gene predictions and conservation scores [1].
The track line, field by field
A custom track begins with a single line of text describing the file. For a BAM, the minimum is a track type and a data URL.
track type=bam name="Sample A alignments" description="hg38, duplicate-marked" db=hg38 bigDataUrl=https://data.example.org/bams/sampleA.bam visibility=full color=0,0,178 height=120 autoScale=on
Read it left to right. type=bam tells the browser which parser to use. name is the label in the track list and must be wrapped in double quotes if it contains spaces. description appears to the right of the label. db=hg38 pins the track to an assembly, which prevents it from appearing on the wrong genome. bigDataUrl is the only field that must be exactly right, character for character. visibility=full shows reads as individual boxes. color sets the read color as three comma-separated integers from 0 to 255. height sets the track height in pixels. autoScale=on lets the coverage histogram adjust its vertical range to the visible window.
If you have ever pasted a track line and watched the browser accept it silently while nothing appeared, the cause is usually a malformed or mismatched bigDataUrl. The browser does not always surface a clear error for a URL it cannot resolve.
The index naming rule that trips up most people
The index file for a BAM is conventionally named with .bai appended to the full BAM file name. A BAM called sampleA.bam needs an index called sampleA.bam.bai. Many alignment pipelines write sampleA.bai instead, dropping the .bam portion of the stem.
UCSC derives the index location from the BAM URL by appending .bai. If your index is named sampleA.bai, the browser looks for sampleA.bam.bai, finds nothing, and the track fails. The fix is a rename on the server.
mv sampleA.bai sampleA.bam.bai
This single renaming step resolves a large share of "my BAM will not display" reports. It costs nothing and should be the first thing you verify.
Preparing the BAM Before You Point the Browser at It
Five checks take about two minutes with samtools and prevent nearly every avoidable failure. Run them before you construct the track line, not after.
Step 1: confirm the file is intact
samtools quickcheck -v sampleA.bam
A healthy file produces no output and exits cleanly. The -v flag makes the tool report which checks failed. A truncated BAM, common after an interrupted transfer, will fail here. The usual cause is a copy that stopped partway, and the usual fix is to re-transfer and compare file sizes between source and destination.
Step 2: confirm coordinate sorting
samtools view -H sampleA.bam | head -5
Look at the @HD line. You want SO:coordinate. If it says SO:unsorted or SO:queryname, the file cannot be indexed for regional access and must be re-sorted.
samtools sort -@ 8 -m 2G -o sampleA.sorted.bam sampleA.bam
The -@ flag sets the number of threads. The -m flag sets memory per thread. Both are worth raising on a machine with resources available, because sorting is the slowest step in this workflow for large files.
Step 3: build the index
samtools index sampleA.sorted.bam
This writes sampleA.sorted.bam.bai in the same directory. Confirm with ls -lh that the index exists and is not zero bytes. Note that samtools index produces the correct double-extension name by default, which is one reason to re-index rather than rename a stray index produced by other software.
Step 4: read the @SQ header and match it to the assembly
samtools view -H sampleA.sorted.bam | grep '^@SQ'
Each @SQ line gives a sequence name (SN) and a length (LN). These are your ground truth for assembly matching. For hg19, chromosome 1 is 249,250,621 bases. For hg38, chromosome 1 is 248,956,422 bases. If the LN value in your header matches neither, your reference is something else, and you need to identify it before you pick a db value.
samtools idxstats sampleA.sorted.bam
This prints one line per reference sequence with the number of mapped and unmapped reads. It is the fastest way to see the exact chromosome names your BAM uses, which matters for the next step.
Step 5: decide whether to subset
A whole-genome BAM with hundreds of millions of reads is slow to browse and visually unreadable at most zoom levels. If the goal is to inspect one locus, subset the file.
samtools view -b sampleA.sorted.bam chr17:7,000,000-7,200,000 > locus.bam
samtools index locus.bam
The subset file is small, loads instantly, and still carries the same header, so it stays compatible with the same db value. For a publication figure, a locus subset is often better than the full file because every read in view is relevant.
How to Upload a BAM to the UCSC Genome Browser
The word "upload" is misleading for BAM files. The browser does not accept a BAM from your local disk into a permanent store. It accepts a URL pointing at a BAM on a web server that you control or that a repository provides. Everything below assumes that hosting step is done.
The quick route: paste a track line
- Open the Genome Browser and select the assembly that matches your BAM header.
- Navigate to the region you want to inspect, so the browser has a locus to display.
- Choose the option to add custom tracks.
- Paste the track line into the text box, with your real
bigDataUrl. - Submit, then return to the browser view.
If the track appears in the track list but the canvas stays empty, move to the troubleshooting table below. If the track does not appear in the list at all, the track line was rejected and the error message usually names the offending field.
The durable route: a data hub
A data hub is a small set of plain text files that describe many tracks organized by genome. It is the right choice when you have more than a handful of BAMs, when you want to share a stable configuration with collaborators, or when you want the tracks to survive across sessions.
The hub needs three files at a minimum. The entry point lists the hub itself.
hub SampleHub
shortLabel Sample hub
longLabel Sample alignment hub for hg38
genomesFile genomes.txt
email [email protected]
descriptionUrl about.html
The genomes file maps an assembly to its track database.
genome hg38
trackDb hg38/trackDb.txt
The track database holds the track definitions, in the same key-value style as a custom track line but without the leading track keyword and type= syntax.
track sampleA
bigDataUrl https://data.example.org/bams/sampleA.bam
shortLabel Sample A
longLabel Sample A alignments, hg38
type bam
visibility full
Point the browser at the URL of hub.txt and it will load the whole set. A hub makes it easy to add a second sample later without rebuilding a track line by hand, and it separates the file inventory from the display settings.
Hosting that actually works
Four hosting details decide whether the browser can read your file.
The URL must be public. A link that requires a login, a VPN, or a browser cookie will fail, because the Genome Browser fetches the file from its own infrastructure rather than from your logged-in session.
The server must support HTTP range requests. The browser requests a byte range for the visible window, not the entire file. Object storage services and most well-configured web servers handle this. A minimal development server often does not.
The URL must use HTTPS if the browser page does. A BAM served over plain HTTP from an HTTPS page is blocked as mixed content, and the track fails without a useful error.
The file and index must be readable. Permission errors on the server look identical to missing files from the browser's side.
Safety note. A BAM from a human subject contains reads that can be traced back to identifiable sequence. Placing that file on a public URL makes it downloadable by anyone who has the link, and search engines can index open directories. Use a controlled-access repository, an unguessable path with access controls, or a de-identified subset. Never publish a clinical BAM to a public bucket as a shortcut.
Troubleshooting Table
| Symptom | Likely cause | How to confirm | Fix |
|---|---|---|---|
| Track loads but the canvas is blank at every zoom level | Index missing or misnamed | Request the bigDataUrl plus .bai in a browser tab, expect a download rather than a 404 | Rename sample.bai to sample.bam.bai, or re-index with samtools |
| Error says the BAM or index cannot be opened | URL wrong, private, or the path has a typo | Paste the exact URL into a private browser window | Correct the path, fix permissions, move to public hosting |
| Reads appear shifted, or reads vanish near chromosome ends | BAM aligned to a different assembly | Compare @SQ LN values against the assembly chromosome lengths | Re-align to the matching reference, or change the db value |
| Some chromosomes show reads and others are empty | Contig naming mismatch, typically 1 versus chr1 | samtools idxstats shows the names in use | Re-align against a chr-prefixed reference, or rewrite names with a tool that updates both header and records |
| "Error reading BAM file" partway along a chromosome | Truncated or corrupted file | samtools quickcheck -v on the hosted copy | Re-transfer, verify checksums, re-index |
| Browser becomes sluggish or unresponsive at wide zoom | Very large file with high read density | Check file size and total read count | Subset to a locus, downsample, or show coverage as bigWig instead |
| Reads display but no coverage histogram | Visibility or display mode setting | Inspect the track line for visibility | Set visibility=full or switch to a density-style display |
| Track never loads and the console reports a blocked request | Mixed HTTP and HTTPS content | Inspect the URL scheme in the track line | Re-host under HTTPS |
| Index exists and the URL is correct but reads still fail | Server does not honor range requests | Check response headers for Accept-Ranges | Move the file to object storage or a range-capable web server |
| Track line submission is rejected outright | Syntax error, missing quotes, unsupported key | Compare against a known-good track line | Fix quoting and spacing, remove unrecognized keys |
Causes, From Most Common to Rarest
Missing or misnamed index
The BAM index is a binning structure that maps genomic intervals to byte offsets in the compressed file. Without it, the browser would have to scan the whole file to find reads for a region, which is impractical over a network. The index is not optional.
The naming convention is the failure point, not the indexing itself. Many variant callers and some aligners emit sample.bai rather than sample.bam.bai. A second failure mode is an index built for a different version of the BAM, which happens after a re-sort that overwrites the BAM but leaves a stale index in place. Always re-index after any operation that rewrites the file.
Assembly mismatch
A BAM aligned to hg19 and displayed on hg38 produces reads at coordinates that do not correspond to the annotation underneath. Chromosome lengths differ between the two assemblies, so reads near the end of a chromosome may fall outside the reference and fail to load. Small reference differences also shift coordinates locally, which makes a variant look mispositioned relative to the gene model.
The @SQ header is the authoritative record of which reference was used. Compare its lengths to the assembly you selected. If they disagree, choose the correct assembly or re-align. Do not assume that a file named hg38 is actually hg38.
Contig naming mismatch
UCSC assemblies use names like chr1, chrX, and chrM. Some reference sources use 1, X, and MT. A BAM header that says SN:1 will never match a browser view of chr1, and the reads simply do not appear.
Evidence comes from samtools idxstats, which lists the names exactly as stored. The durable fix is to re-align against a reference with the same naming scheme as the target assembly. Renaming only the header is not enough, because each alignment record stores its own reference name in the RNAME field. You need a tool or pipeline that rewrites both, and re-alignment is usually the least error-prone route.
Not coordinate sorted
An indexed BAM must have its records grouped by reference and ordered by position. A query-name-sorted or unsorted BAM cannot be indexed meaningfully. Sorting is quick compared with alignment and is worth doing routinely as the final step of any alignment pipeline.
URL is not publicly reachable
Internal file servers, S3 buckets with private ACLs, and paths that depend on an authenticated session all fail the same way. The Genome Browser's servers fetch the file, not your browser tab, so a URL that works for you may still fail for the track.
Server does not support range requests
This cause is rare with commercial cloud storage and more common with hand-rolled servers. The symptom is a track that loads its header and then stalls or errors when you pan. Testing a range request with a command-line client and inspecting the response headers settles it quickly.
Track line syntax
A missing quote around a name with spaces, a stray character in the URL, or an unsupported key can make the whole line invalid. The browser will sometimes accept a malformed line and then behave unpredictably. Start from a known-good line and change one field at a time.
Mixed content and browser security
An HTTPS browser page will not load an HTTP data file. This is a browser-level policy, not a UCSC bug. Hosting the BAM under HTTPS resolves it.
File size and read density
A BAM with deep coverage at a locus produces thousands of overlapping reads that no display can render usefully. The browser limits how much data it draws in a window, and very wide views become slow. Two solutions work well. Subset the BAM to the region of interest, or summarize coverage into a bigWig track and keep the BAM for base-level inspection. A bigWig file stores signal values at fixed intervals and is supported by many genome browsers, which makes it a portable companion to the alignment file [3].
Corruption and truncation
Interrupted transfers, full disks, and interrupted compression produce BAM files that pass a casual ls check and fail on read. samtools quickcheck exists precisely for this. Run it on the hosted copy, not just the local original, because the transfer is where damage usually occurs.
A Short Diagnostic Sequence
Work through these five checks in order. Each one isolates a different layer, so the result tells you which category of cause you are in.
- Request the BAM URL in a browser tab. A download means hosting works. A 403 or 404 means it does not.
- Request the same URL with
.baiappended. A download means the index is findable. A 404 means the name is wrong or the file is missing. - Run
samtools quickcheck -von the hosted copy. Silence means the file is intact. - Run
samtools idxstatsand compare the names to the browser's chromosome names. A prefix difference means a naming mismatch. - Run
samtools view -Hand compareLNvalues to the assembly lengths. A mismatch means the wrong assembly is selected.
If all five pass and the track is still blank, the problem is in the track line itself. Rebuild it from a minimal version with only type, name, db, and bigDataUrl, confirm that it renders, then add display settings back one at a time.
Where BAM Fits Among Bioinformatics File Formats
Understanding the surrounding formats makes BAM problems easier to diagnose, because many failures are really format-conversion problems in disguise.
FASTQ vs FASTA
FASTA and FASTQ both store sequence, and confusing them is one of the most common early mistakes in a sequencing project.
FASTA uses a header line beginning with > followed by the sequence, typically wrapped across multiple lines. It carries no quality information. Reference genomes are distributed as FASTA, and a reference FASTA is the file you align against. Tools that index a reference for random access, such as samtools faidx, operate on FASTA.
FASTQ uses four lines per record: an identifier beginning with @, the sequence, a separator line beginning with +, and a string of ASCII characters that encode per-base quality scores. The quality string is the defining feature. It preserves the confidence the sequencer assigned to each base, and that information flows through alignment into the BAM as the QUAL field.
The practical rule is simple. The reference is FASTA. The reads are FASTQ. The alignment of one to the other is SAM, and its compressed binary form is BAM. If you are ever asked to "convert FASTQ to BAM," what is actually meant is align FASTQ reads to a FASTA reference and write the result as BAM. There is no direct format conversion that skips alignment.
Textbook treatments of sequencing file formats emphasize this same division of labor, with quality-scored reads on one side and reference sequence on the other, and the alignment file as the bridge between them [4].
SAM, BAM and CRAM
SAM is the plain text alignment format. It is human readable, which makes it useful for debugging a header, and far too large for routine storage. BAM is the same content in a compact binary encoding, blocked and compressed so that a region can be fetched without reading the whole file.
CRAM is a newer compressed alignment format that stores reads relative to the reference sequence, which reduces size substantially. CRAM requires access to the reference to reconstruct reads, which adds a dependency that BAM does not have. If your data are in CRAM, convert to BAM before building a browser track, since BAM is the format the custom track system is built around [1].
Both BAM and VCF are routinely accepted as inputs by downstream tools, and the pair has become the standard working set for variant and alignment work [5]. The BAM holds the reads, and the VCF holds the interpreted variants.
Coverage and annotation formats the browser also accepts
A BAM is the right file for inspecting individual reads, splice junctions, and mismatches. It is the wrong file for a genome-wide coverage overview. For that, convert coverage to bigWig. The bigWig format stores a signal track indexed by genomic interval and is supported across browser platforms [3].
Genome browsers also accept annotation formats. BED and bigBed describe intervals such as peaks or called regions. GFF3 and GTF describe gene models with hierarchical feature relationships. VCF describes variants with respect to a reference. Mutation summary tools commonly accept VCF, MAF, and TSV inputs, which reflects how often variant tables are reshaped between analyses [6]. Annotation conversion tools frequently emit GFF3 precisely because it can be dropped into a genome browser without further processing [7]. Older annotation exchange still relies on GenBank flat files for sequence and feature records [8].
A BAM track sits alongside these annotation tracks and inherits their coordinate system. That is why the assembly match matters so much. Every track on the screen is interpreted against the same reference.
Common Mistakes and Limitations
Displaying BAM data in a browser is a visualization step, not an analysis step. It shows what the aligner produced. It does not tell you whether the alignment is correct, whether the library preparation was sound, or whether a variant is real. Treat the track as a view of evidence and confirm calls with dedicated tools.
Uploading a BAM and forgetting the index is the single most common mistake. The second most common is treating the browser as a file host. It is not. The file lives on your server, and the browser reads slices of it.
Publishing a whole-genome BAM for a figure is usually the wrong choice. The file is large, slow to render, and contains far more information than the figure conveys. Subset to the locus and keep the full file in an archive.
Assuming a file name describes its contents is a frequent and expensive error. A BAM named sample_hg38.bam may have been aligned to hg19 and renamed during a handoff. Only the @SQ header settles the question.
Ignoring chromosome naming until the track fails wastes time. Check samtools idxstats early in the project, before alignment if possible, and standardize on the naming scheme of the assembly you plan to browse.
Expecting the browser to render every read at every zoom level is unrealistic. At whole-chromosome scale, individual reads are smaller than a pixel. Use a coverage track for the overview and the BAM for the detail.
The UCSC custom track and data hub system is built for browsing, and it works best with files hosted on infrastructure designed for range requests [2]. A departmental file server configured for document sharing is often a poor fit. When the hosting requirement becomes a recurring obstacle, a locally deployed browser that reads files from disk is a reasonable alternative. JBrowse 2, for example, is a modular browser that loads a FASTA reference genome and GFF3 annotation directly and is designed for deployment on a web server [9]. The tradeoff is setup effort against long-term convenience.
Finally, the browser shows alignments, not raw signal. If you need to inspect base-level quality, per-read flags, or soft-clipped sequences, you need a tool that exposes record fields directly. The browser is a summary view by design [4].
Preventing the Next Failure
Build the BAM so that it is browser-ready from the start. Sort it, index it with the correct double extension, and confirm the header before the file leaves the analysis machine.
Standardize chromosome naming across the project. If you plan to browse on UCSC, align against a reference that uses chr prefixes. Document that decision in the project README so the next person does not reverse it.
Keep a small subset BAM for every locus you plan to show. Regenerate subsets when the underlying alignment changes, and never let a stale subset drift from its source.
Host on infrastructure that supports range requests and HTTPS. Object storage with a controlled bucket policy covers both requirements and removes a whole class of intermittent failures.
Record the assembly and reference build in the file name and in the track description. A description field that says hg38, GRCh38.p14 reference, duplicate-marked saves a future reader from guessing.
Test with a minimal track line before adding display settings. A four-field track that works is a solid foundation. A twenty-field track that fails is a debugging puzzle.
Frequently Asked Questions
Can I upload a BAM directly from my laptop to UCSC?
No. The browser reads BAM files over HTTP or HTTPS by byte range, so the file and its index must be hosted at a public URL. Local paths and authenticated links do not work.
Why does my BAM track show nothing when the file clearly exists?
The most common reason is a missing or misnamed index. The browser expects the index at the BAM URL with .bai appended, so a file named sample.bai must be renamed to sample.bam.bai.
Do I need a .bai file, and where does it go?
Yes. The index must sit in the same directory as the BAM with the double extension. Without it, the browser cannot locate reads for a region and the track will not render.
My BAM uses "1" instead of "chr1". What do I do?
Re-align against a reference that uses the chr naming scheme. Renaming only the header is insufficient because every alignment record stores its own reference name, and a mismatch between header and records creates a new problem.
Can I display CRAM instead of BAM?
Convert CRAM to BAM first. The custom track system is built around BAM, and converting with samtools gives you a file that the browser can index and read normally.
Why is my BAM track so slow?
Large files with deep coverage overwhelm the display at wide zoom levels. Subset the BAM to the region of interest, and use a bigWig coverage track for the genome-wide overview.
Can I visualize a BAM aligned to a non-human genome?
Yes, as long as that assembly is available in the browser and the BAM header matches its sequence names and lengths. Check the @SQ lines against the assembly before building the track.
What is the difference between a BAM track and a bigWig track?
A BAM track shows individual reads with their alignment details. A bigWig track shows a continuous signal such as coverage or enrichment. Use BAM for read-level inspection and bigWig for summary views.
Sources
- The UCSC Genome Browser database: update 2011.
- The UCSC genome browser and associated tools.
- Prioritising positively selected variants in whole-genome sequencing data using FineMAV.
- Online resources for genomic analysis using high-throughput sequencing.
- pyCancerSig: subclassifying human cancer with comprehensive single nucleotide, structural and microsatellite mutational signature deconstruction from whole genome sequencing.
- CoMutPlotter: a web tool for visual summary of mutations in cancer cohorts.
- BioNanoAnalyst: a visualisation tool to assess genome assembly quality using BioNano data.
- Circleator: flexible circular visualization of genome-associated data with BioPerl and SVG.
- Setting Up the JBrowse 2 Genome Browser.