VCF Annotation Tools: What to Run on Your Own Genome
If you have a whole-genome VCF and want it annotated, run three things in order: bcftools norm to split multiallelics and left-align indels, Ensembl VEP in offline cache mode to attach transcript consequences and population frequencies, and vcfanno or slivar to join any dataset VEP does not carry. Everything else (ANNOVAR, SnpEff, web uploaders, GUI browsers) is a substitution for one of those three steps or a viewer bolted on top. The annotation itself is cheap. The work is in normalization, choosing which transcript’s consequence you believe, and filtering 4-5 million variants down to the few hundred you will look at.
Normalize before you annotate
This is where most personal-genome analyses go wrong, quietly, and the result looks fine.
A single-sample 30x WGS VCF typically holds roughly 4.5-5 million variants, of which about 4-4.5 million are SNVs and 500-800k are indels. Any of those indels can be written several ways. GATTACA -> GATACA and ATT -> AT at a shifted coordinate are the same deletion. Annotation databases store one representation. If yours differs, the join silently returns nothing and you conclude a variant is novel when gnomAD has it at 12% allele frequency.
bcftools norm -m -any -f GRCh38.p14.genome.fa -c w \
-Oz -o sample.norm.vcf.gz sample.vcf.gz
bcftools index -t sample.norm.vcf.gz
-m -any splits multiallelic sites into one record per ALT, which every downstream annotator handles better than a comma-separated ALT field. -f supplies the reference so indels are left-aligned and trimmed. -c w warns on REF mismatches instead of failing, which is how you discover you are annotating a GRCh37 VCF against a GRCh38 FASTA.
Three more failure modes worth checking before you spend an hour on annotation:
- Contig naming. gnomAD v4 and the Ensembl GRCh38 cache use
chr1. Some pipelines emit1. Fix withbcftools annotate --rename-chrs. - Assembly. Check the
##referenceheader line and spot-check a known position.rs334(HBB) sits at chr11:5,227,002 on GRCh38 and 11:5,248,232 on GRCh37. If your coordinates match neither, you have a liftover problem, not an annotation problem. - Filter status. Most consumer VCFs ship every call, PASS or not.
bcftools view -f PASSbefore annotating cuts runtime and removes a large share of the junk.
The annotator: VEP offline
We use Ensembl VEP, installed locally with a cache, and we do not use the web interface for whole genomes.
vep \
-i sample.norm.vcf.gz -o sample.vep.vcf.gz \
--vcf --compress_output bgzip \
--offline --cache --dir_cache $HOME/.vep --assembly GRCh38 \
--fasta GRCh38.p14.genome.fa \
--fork 8 --buffer_size 50000 \
--symbol --hgvsg --hgvsc --hgvsp --numbers --canonical --mane_select \
--pick_allele_gene \
--af_gnomadg --af_gnomade \
--plugin dbNSFP,/data/dbNSFP4.7a.gz,REVEL_score,CADD_phred,SIFT_pred,MetaRNN_score \
--plugin SpliceAI,snv=spliceai_snv.hg38.vcf.gz,indel=spliceai_indel.hg38.vcf.gz
Why these choices:
--offline --cache keeps everything local and reproducible. The GRCh38 merged cache is about 25 GB on disk. Expect 30-90 minutes for a full genome with --fork 8 on a laptop-class machine, dominated by the plugin lookups, not VEP itself. dbNSFP alone is ~35 GB compressed.
--pick_allele_gene matters more than people expect. A variant in a gene with 14 annotated transcripts produces 14 consequence blocks. Without a picking rule you get a CSQ field with dozens of comma-separated entries and no obvious way to sort them. --pick collapses to one consequence per variant, which loses genes at overlapping loci. --pick_allele_gene gives one per allele per gene, which is the right granularity. Add --flag_pick instead if you want everything retained with the chosen one marked.
--mane_select tags the MANE Select transcript, the RefSeq/Ensembl-matched representative transcript. When a clinical report and your own analysis disagree on an HGVS coding change, the cause is usually a different transcript. Report the transcript ID with every variant you care about.
ANNOVAR is the main alternative and is faster on large cohorts, with a table-based table_annovar.pl workflow that emits a flat TSV rather than stuffing everything into INFO 1. If you prefer TSV output and a simpler mental model, it is a reasonable pick. We stay with VEP because the plugin ecosystem (SpliceAI, dbNSFP, CADD, LoFtee) is better maintained and because VEP’s consequence assignment against Ensembl transcripts is the thing most downstream tools assume. SnpEff is a third option, fast and dependency-light, weaker on frequency and pathogenicity databases.
For anything VEP does not carry, use vcfanno. It takes a TOML config, joins INFO fields from arbitrary bgzipped, tabix-indexed VCFs or BEDs, and runs at a few hundred thousand variants per second:
[[annotation]]
file="clinvar_20250501.vcf.gz"
fields=["CLNSIG", "CLNREVSTAT", "CLNDN"]
ops=["self", "self", "self"]
names=["clinvar_sig", "clinvar_stars", "clinvar_disease"]
ClinVar specifically should be re-downloaded and re-run every few months. Classifications change, and a variant labeled “uncertain significance” in 2023 may be reclassified now.
Filtering down to something readable
Annotation gives you 5 million rows with 60 INFO fields. The reduction is the analysis. slivar is the tool we reach for: it compiles JavaScript expressions over VCF records and runs them fast enough for whole genomes, and it sits in the same family of small, composable VCF tools as vcflib, cyvcf2, and hts-nim 2.
slivar expr --vcf sample.vep.vcf.gz \
--gnotate gnomad.v4.hg38.zip \
--info 'variant.FILTER == "PASS" && INFO.gnomad_popmax_af < 0.001' \
--pass-only -o rare.vcf.gz
Then extract a flat table with bcftools +split-vep, which parses the packed CSQ field into columns:
bcftools +split-vep rare.vcf.gz \
-f '%CHROM\t%POS\t%REF\t%ALT\t%SYMBOL\t%Consequence\t%HGVSp\t%REVEL_score\t%CADD_phred\n' \
-s worst -i 'Consequence ~ "missense_variant|stop_gained|frameshift"' \
> candidates.tsv
From a full genome, a popmax AF cutoff of 0.1% plus a coding-consequence filter usually leaves a few hundred to a couple thousand rows. That is a spreadsheet, not a dataset.
If you want a pipeline that bundles annotation and filtration rather than composing your own, VarAFT combines VEP-based annotation with a filtering interface aimed at exome and genome data 3, and WGSA was built specifically to annotate whole-genome variant sets, including non-coding positions, at scale 4. Both trade flexibility for fewer moving parts.
Viewing and browsing
bcftools view file.vcf.gz | less -S answers most questions. For anything more, pick by the question:
- Inspecting a specific locus with read-level evidence: IGV, loading the VCF and the BAM/CRAM together. This is the only way to tell a real indel from an alignment artifact.
- Interactive browsing across a whole genome with annotation tracks: BasePlayer, which was designed for exactly this and handles both coding and noncoding variants in large sample sets 5.
- Building filters and saved queries without writing code: VCF-Miner provides a GUI over annotated VCFs with group-based filtering 6.
- Quick sanity checks on a new file, or comparing VCFs from two callers: VCF observer handles preliminary comparison and summary of multiple VCFs 7.
- Figures: VIVA generates heatmaps and per-sample plots directly from VCF 8, and CircosVCF draws genome-wide circular views of variation 9.
We do not put much weight on genome-wide visualizations for a single person. One genome has no contrast to show. They earn their keep in cohorts.
What annotation tells you and what it does not
An annotated VCF is a set of predictions and database lookups. CADD and REVEL are scores trained on prior data, not measurements of what a variant does in you. ClinVar entries carry review status, and a one-star submission with a single submitter is not evidence. Coverage matters: a variant absent from your VCF might be absent from your genome, or the caller might have had four reads there. Check depth (FORMAT/DP) before believing an absence.
Nothing produced by this pipeline is a diagnosis. If a variant in a medically relevant gene survives your filters, the next step is a clinician and, in most cases, orthogonal confirmation in a clinical lab, because research-grade calls carry error rates that clinical reporting does not accept.
Questions people also ask
How do I view a VCF file on my computer? For a quick look, bcftools view -h file.vcf.gz for the header and bcftools view -H file.vcf.gz | head for records, piped through less -S to stop line wrapping. Never open a whole-genome VCF in Excel. For a specific region with read evidence, load the VCF and the aligned BAM in IGV.
What is the best tool for annotation? Ensembl VEP run offline with a local cache, for the plugin ecosystem and the transcript handling. ANNOVAR is the strongest alternative and produces flat TSV output that is easier to pipe into pandas 1. SnpEff is the fastest to set up if you only want consequence calls.
How can I analyze a VCF file? Normalize, annotate, filter, then read. In Python, cyvcf2 gives you fast record-level iteration over a compressed VCF without shelling out, and it belongs to the same set of tools built around htslib for exactly this purpose 2. In shell, bcftools query and bcftools +split-vep cover most extractions.
What is a sequence annotation? Attaching interpretation to a coordinate: which gene and transcript the position falls in, what the change does to the protein, how often the allele appears in reference populations, and what any curated database says about it. The VCF’s INFO column is the container for all of it.
What are annotation tools? Programs that join a variant list against reference resources and write the result back into the file. VEP, ANNOVAR, SnpEff, vcfanno, and slivar are the ones in regular use. Pipelines like WGSA package several of these together for whole-genome scale 4, and variant tools takes a database-backed approach to storing and querying annotated variants across projects 10.
Oak builds longitudinal molecular profiles of individuals: whole-genome sequencing, RNA sequencing, proteomics, blood biomarkers, and continuous glucose data, integrated into one model of you. Build your profile.
Footnotes
-
Hui Yang, Kai Wang. Genomic variant annotation and prioritization with ANNOVAR and wANNOVAR. Nature Protocols, 2015. https://doi.org/10.1038/nprot.2015.105 ↩ ↩2
-
Erik Garrison, Zev N. Kronenberg, Eric T. Dawson, et al. A spectrum of free software tools for processing the VCF variant call format: vcflib, bio-vcf, cyvcf2, hts-nim and slivar. PLOS Computational Biology, 2022. https://doi.org/10.1371/journal.pcbi.1009123 ↩ ↩2
-
Jean-Pierre Desvignes, Marc Bartoli, Valérie Delague, et al. VarAFT: a variant annotation and filtration system for human next generation sequencing data. Nucleic Acids Research, 2018. https://doi.org/10.1093/nar/gky471 ↩
-
Xiaoming Liu, Simon White, Bo Peng, et al. WGSA: an annotation pipeline for human genome sequencing studies. Journal of Medical Genetics, 2015. https://doi.org/10.1136/jmedgenet-2015-103423 ↩ ↩2
-
Riku Katainen, Iikki Donner, Tatiana Cajuso, et al. Discovery of potential causative mutations in human coding and noncoding genome with the interactive software BasePlayer. Nature Protocols, 2018. https://doi.org/10.1038/s41596-018-0052-3 ↩
-
Steven N. Hart, Patrick Duffy, Daniel J. Quest, et al. VCF-Miner: GUI-based application for mining variants and annotations stored in VCF files. Briefings in Bioinformatics, 2015. https://doi.org/10.1093/bib/bbv051 ↩
-
Abdullah Asım Emül, Mehmet Arif Ergün, Rumeysa Aslıhan Ertürk, et al. VCF observer: a user-friendly software tool for preliminary VCF file analysis and comparison. BMC Bioinformatics, 2024. https://doi.org/10.1186/s12859-024-05860-0 ↩
-
G. A. Tollefson, J. Schuster, F. Gelin, et al. VIVA (VIsualization of VAriants): A VCF File Visualization Tool. Scientific Reports, 2019. https://doi.org/10.1038/s41598-019-49114-z ↩
-
E Drori, D Levy, P Smirin-Yosef, et al. CircosVCF: circos visualization of whole-genome sequence variations stored in VCF files. Bioinformatics, 2017. https://doi.org/10.1093/bioinformatics/btw834 ↩
-
F. Anthony San Lucas, Gao Wang, Paul Scheet, et al. Integrated annotation and analysis of genetic variants from next-generation sequencing studies with variant tools. Bioinformatics, 2011. https://doi.org/10.1093/bioinformatics/btr667 ↩