Back to Blog
VisualizationGenome Browser

Install IGV and igvtools to View BAMs and VCFs Locally (macOS, conda)

Launch the IGV desktop genome browser from a conda env, index BAM and VCF files with igvtools, and browse alignments and variants offline.

SSSudipta SardarJuly 20, 20267 min read
Install IGV and igvtools to View BAMs and VCFs Locally (macOS, conda)

Once you have a sorted BAM or a called VCF, the fastest way to sanity-check it is to actually look at it. The Integrative Genomics Viewer (IGV) is the standard desktop app for that: point it at a reference genome, drag in your alignments and variants, and scroll around like a genome-scale spreadsheet. This guide installs IGV and its command-line companion, igvtools, into an isolated conda environment on macOS, so the whole toolchain lives in one place and never touches your system Java.

What IGV and igvtools actually do

IGV is a Java desktop application, not a library you import. When you run it, a window opens with a chromosome ideogram at the top and stacked tracks below it, and you interact with it by clicking and scrolling rather than typing commands. It reads BAM, CRAM, VCF, BED, GFF3, BigWig and several other formats directly, and ships with a set of built-in reference genomes (human, mouse, and more) that you can pick from a dropdown instead of downloading yourself.

igvtools is a separate command-line package built by the same team. It does the boring but necessary work that makes IGV fast: sorting alignment files, building .idx indexes for feature files, and precomputing coverage tracks in IGV's own tiled data format (.tdf) so that zooming out over a whole chromosome does not mean re-scanning a 20 GB BAM on every redraw.

On bioconda, igv and igvtools are two separate packages, not one. You install both into the same environment to get the viewer and its indexing helper together.

Prerequisites

You need a working conda on your Mac. If you do not have one yet, start with our Miniconda on Apple Silicon guide and come back here. Both packages are Java-based and pull in openjdk automatically, so you do not need a system-wide JDK install — conda manages that for you inside the environment.

We install only from conda-forge and bioconda, and we pass --override-channels so conda never touches the Anaconda defaults channel, which on recent conda versions is gated behind a Terms-of-Service prompt.

TL;DR: copy-paste install

bash
conda create -n bu-igv --override-channels -c conda-forge -c bioconda igv igvtools
conda activate bu-igv
igv --version
igvtools help

That gets you a working viewer and indexer. The rest of this guide walks through each step and what to expect.

Step 1: Create an isolated environment

We name the environment bu-igv and ask for both igv and igvtools explicitly in one solve, so conda picks an openjdk version both packages agree on.

bash
conda create -n bu-igv --override-channels -c conda-forge -c bioconda igv igvtools

Conda solves the environment and pulls in a matching JDK; you should see something like this in the install plan before it downloads anything:

The following NEW packages will be INSTALLED:

  igv          bioconda/noarch::igv-2.19.8-*
  igvtools     bioconda/noarch::igvtools-2.17.3-*
  openjdk      conda-forge/osx-arm64::openjdk-21.*
  ...

Both packages are published as noarch, meaning the same build works on Apple Silicon and Intel Macs alike — there is no arm64-specific build to hunt for and no Rosetta 2 fallback to worry about, because the payload is portable Java bytecode, not compiled native code.

igv currently pins openjdk <22 while igvtools needs openjdk >=17. Installing them together lets conda's solver pick a JDK in that overlapping range (typically 17–21) automatically. If you separately pin openjdk to a version outside that window, the solve will fail with an unsatisfiable-package error — drop the pin and let conda choose.

Step 2: Verify the install

Activate the environment and check both tools respond:

bash
conda activate bu-igv
igv --version
igvtools help

igv --version should print an IGV version string without opening the GUI. igvtools help prints the list of subcommands (sort, index, count, tile, toTDF, and more) rather than launching anything graphical, so both checks are safe to run over SSH or in a script.

Also confirm the binaries resolve inside your environment:

bash
which igv igvtools

Both should point inside .../envs/bu-igv/bin/. If you get command not found, activate the environment first.

Step 3: Index your BAM and VCF before loading them

IGV expects alignment and variant files to be indexed so it can jump to a region without reading the whole file. A BAM needs a coordinate-sorted, .bai-indexed companion; the standard way to produce that is samtools sort and samtools index, covered in our Install SAMtools and BCFtools guide:

bash
samtools sort -o aligned.sorted.bam aligned.bam
samtools index aligned.sorted.bam

For a VCF, igvtools index builds the companion index IGV needs directly:

bash
igvtools index variants.vcf

This writes variants.vcf.idx next to the original file. If your VCF came out of a variant caller such as the one in our Install GATK4 guide, it is usually already sorted, so indexing is normally the only step left before loading it.

For large BAMs, precomputing a coverage track speeds up whole-chromosome browsing considerably:

bash
igvtools count aligned.sorted.bam aligned.sorted.bam.tdf hg38

igvtools count bins read coverage across the genome and writes a .tdf file. Loading the .tdf alongside the BAM gives you a smooth coverage histogram at any zoom level instead of IGV recomputing it from raw reads every time you scroll.

Step 4: Launch IGV and load your data

Start the GUI from the activated environment:

bash
igv

A window opens with a genome selector in the top-left corner. From there:

  1. Pick a built-in reference from the dropdown (for example hg38), or choose Genomes → Load Genome from File to use your own FASTA.
  2. Use File → Load from File and select your indexed BAM or VCF. IGV finds the .bai/.idx automatically as long as it sits next to the data file with a matching name.
  3. Type a gene name or chr:start-end coordinates into the locus box and press Enter to jump straight to a region of interest.

Alignment tracks show read pileups with mismatches highlighted against the reference, and VCF tracks show one row per variant with a color-coded genotype track underneath if your VCF includes sample columns. This is where a lot of "did my pipeline actually do what I think it did" questions get answered in ten seconds instead of ten minutes of parsing files by hand.

Common errors and fixes

ErrorFix
CondaToSNonInteractiveError / Terms of Service not accepted for defaultsDo not use the defaults channel. Install with --override-channels -c conda-forge -c bioconda as shown.
Conda solve fails with an unsatisfiable openjdk constraintYou (or another package) pinned openjdk outside the 17–21 range both igv and igvtools need. Remove the pin and let conda choose a compatible build.
igv window never appears, or an AWT/HeadlessException is thrownYou are running over SSH without a display. IGV is a GUI app and needs a local screen, or X11 forwarding (ssh -X) with a working X server. It cannot run in a truly headless shell.
Text and icons look tiny on a Retina displaymacOS scales Retina displays automatically, so IGV usually renders crisply out of the box. If the interface still feels small, raise the font size in IGV's Preferences dialog, or lower the scaled resolution under System Settings → Displays.
igvtools index complains the file is not sortedCoordinate-sort it first. Use samtools sort for BAM, or bcftools sort/sort -k1,1 -k2,2n for VCF and BED, then re-index.
IGV loads the BAM but the track appears emptyConfirm the .bai index exists and matches the loaded BAM's filename exactly, and that you selected the same reference genome build (hg19 vs hg38) that the reads were aligned against.

Managing the environment

Update both tools in place:

bash
conda update -n bu-igv --override-channels -c conda-forge -c bioconda igv igvtools

Snapshot the exact environment so a collaborator can reproduce it:

bash
conda env export -n bu-igv > igv-env.yml

And when you are done, remove it cleanly:

bash
conda remove -n bu-igv --all

Next steps

IGV is where a lot of pipelines get their final human sanity check. A couple of natural follow-ons: