Comparative genomics plays a central role in understanding gene conservation and evolutionary relationships across species. Orthologous genes, which arise from speciation events and often retain similar biological functions, provide a fundamental framework for cross-species genomic comparison. Visualizing these relationships in an intuitive and scalable manner is important for interpreting genome organization and gene correspondence among multiple species.
OrthoRibbon is an R package designed to visualize orthologous gene relationships across species using ribbon-based diagrams. The package connects homologous genes between genomes through ribbons that link their genomic positions, enabling users to easily explore conserved genes and cross-species genomic structures. By transforming ortholog tables into clear visual representations, OrthoRibbon facilitates comparative genomic analysis and produces customizable, publication-quality figures.
OrthoRibbon supports flexible input formats and can integrate ortholog annotations from common databases such as Ensembl or other orthology inference tools. The package is designed to be lightweight and highly customizable, allowing users to control genomic layouts, ribbon aesthetics, and species ordering. These features make OrthoRibbon a practical visualization tool for researchers studying evolutionary conservation, genome organization, and cross-species gene relationships.
There are six main steps to visualize your data with
orthoRibbon:
Step 1. Load the required library.
Step 2. Define the species abbreviation. They are abbreviated species identifiers derived from the binomial (Latin) species names. You can try to guess it from common names. The plot order is decided by the order of your input show as ‘common_name’ here.
common_name <- geneClusterPattern::guessSpecies(
c('human', 'fruit fly'))
names(common_name) <- common_name # must assign the names.
common_name## hsapiens dmelanogaster
## "hsapiens" "dmelanogaster"
Step 3. Retrieve chromosome, homologs, and gene coordinates. You can get the ortholog data via tools like OrthoFinder, odp, or databases like OrthoDB, eggNOG.
extdata <- system.file('extdata', package='orthoRibbon')
chrom_infos <- readRDS(file.path(extdata, 'human_fly/chrom_infos.rds'))
names(chrom_infos)## [1] "hsapiens" "dmelanogaster"
## name length coord_system synonyms toplevel non_ref circular
## 1 KI270510.1 2415 scaffold chrUn_KI.... TRUE FALSE NA
## 2 KI270539.1 993 scaffold chrUn_KI.... TRUE FALSE NA
## the homologs_df must contain columns 'gene_id', and 'ortholog_group'.
homologs_df <- readRDS(file.path(extdata, 'human_fly/homologs_df.rds'))
head(homologs_df, n=2)## gene_id ortholog_group symbol species seq start
## 1 ENSG00000028277 1 POU2F2 hsapiens 19 42086110
## 2 ENSG00000064835 1 POU1F1 hsapiens 3 87259404
Step 4. Prepare the plot data.
If the homologs_df data frame already contains gene
location information (specifically, the “species”, “seq”, and “start”
columns), the genes_gr argument can be omitted. Otherwise,
gene annotations must be provided via the genes_gr argument
as a GRanges object, with “gene_name” and “species” stored
as metadata columns.
plotData <- buildPlotData(common_name, homologs_df,
chrom_infos = chrom_infos,
chromosome_order_method = 'max',
max_links=1000)
names(plotData)## [1] "homolog_df_list" "chrom_bars_df" "chrom_label_df" "symbol_df_top"
## [5] "symbol_df_bottom" "common_name"
# Here subset the plotData to reduce the vignette file size
# This is not necessary for real plot.
plotData <- subset(plotData,
subset=list('hsapiens'=c('1', '2', '3', 'X'),
'dmelanogaster'=c('2L', '2R', 'X')))Step 5. Calculate the exactly position for Bezier curve.
There are three methods for calculating gene positions: chromosome coordinates, gene index, and homologous chromosome. Here we show the example by homologous chromosome and will discuss the details later.
# by homolog chromosomes
bezier_mini <- buildBezierDF(plotData$homolog_df_list,
colname1='topMini_finalOffset',
colname2='bottomMini_finalOffset')Step 6. Plot the data.
OrthoRibbon supports flexible input formats and can integrate ortholog annotations from a variety of sources, including commonly used databases such as Ensembl and results generated by other orthology inference tools. In the quick start section, the data is the output of orthoFinder for human and fruit fly, which is based on ortholog groups information. OrthoRibbon can be readily adapted to using the homolog pairs like data retrieved from Ensembl database.
Here is sample code to prepare the data by retrieving chromosome, homologs and gene coordinates via BioMart.
# prepare mart for BioMart
marts <- geneClusterPattern::guessSpecies(
common_name, output='mart',
host='https://jun2026.archive.ensembl.org')
# prepare all the Ensembl ids
ids <- orthoRibbon::getGeneIDs(common_name, marts)
# get chromosome info
chrom_infos<- lapply(common_name, GenomeInfoDb::getChromInfoFromEnsembl)
# retrieve all the homologs
homologs <- orthoRibbon::getHomologGRs(ids, common_name, marts)
# retrieve all the gene ranges
genes_gr <- orthoRibbon::getGeneGRs(ids, marts, homologs)
# reformat the homologs to data.frame
homologs_df <- getHomologIDs(homologs)Since this step retrieve information via “biomaRt”, it will take some time or out of service.
We will load presaved data.
common_name <- c("hsapiens", "mmusculus", "drerio")
common_name <- setNames(common_name, common_name)
extdata <- system.file('extdata', 'ensembl', package='orthoRibbon')
chrom_infos <- readRDS(file.path(extdata, 'chrom_infos.rds'))
names(chrom_infos)## [1] "hsapiens" "mmusculus" "drerio"
## name length coord_system synonyms toplevel non_ref circular
## 474 19 58617616 chromosome CM000681.... TRUE FALSE NA
## 475 20 64444167 chromosome CM000682.... TRUE FALSE NA
homologs_df <- readRDS(file.path(extdata, 'homolog_df.human.mouse.zebrafish.rds'))
head(homologs_df, n=2)## gene_id1 gene_id2
## 2 ENSG00000091583 ENSMUSG00000000049
## 7 ENSG00000146535 ENSMUSG00000000149
## GRanges object with 2 ranges and 2 metadata columns:
## seqnames ranges strand | gene_name
## <Rle> <IRanges> <Rle> | <character>
## ENSMUSG00000000049 11 108234180-108305222 + | Apoh
## ENSMUSG00000000149 5 140744163-140816186 - | Gna12
## species
## <character>
## ENSMUSG00000000049 mmusculus
## ENSMUSG00000000149 mmusculus
## -------
## seqinfo: 1489 sequences from an unspecified genome; no seqlengths
Setting an appropriate chromosome order can make the data presentation much cleaner by reducing the number of crossing curves and making the overall pattern easier to interpret.
OrthoRibbon provides seven different methods for arranging chromosome order:
You may want to try different ordering methods and compare the resulting visualizations to determine which one provides the clearest representation of your data.
# Different methods optimize different criteria:
# 1. max - minimizes the chromosome distance
chr_order_max <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
chromosome_order_method = "max")
# 2. GW (Gruvaeus-Wainer) - minimizes gradient measure
chr_order_gw <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
chromosome_order_method = "GW")
# 3. OLO (Optimal Leaf Ordering) - optimal for dendrograms
chr_order_olo <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
chromosome_order_method = "OLO")
# 4. TSP (Traveling Salesman Problem) - minimizes path length
chr_order_tsp <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
chromosome_order_method = "TSP")
# 5. Spectral - uses eigenvectors
chr_order_spec <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
chromosome_order_method = "Spectral")
# 6. Spearman distance
chr_order_dist <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
chromosome_order_method = "spearman")
# 7. customized
seq_names <- lapply(chrom_infos, function(.ele) sortSeqlevels(.ele$name))
chr_order_manu <- buildPlotData(common_name, homologs_df, genes_gr, chrom_infos,
chr_orders=seq_names)As describe above, there are three methods for calculating gene positions: chromosome coordinates, gene index, and homologous chromosome.
Chromosome coordinates: Positions are calculated based on the genomic coordinates of the genes. This method emphasizes the actual physical locations and distances between genes on the chromosomes.
Gene index: Positions are based on the rank of each gene along the chromosome, ordered by genomic coordinates. This method emphasizes the organization of homologous gene clusters while ignoring gene lengths and the gaps between genes.
Homologous chromosome: Positions are summarized by the proportion of homologous genes shared between each pair of chromosomes. This method emphasizes the relative proportion of homologous genes between chromosome pairs.
# chr_order_max as plotData from last step
# Where are the homologs of mouse X-linked genes located in the zebrafish and human genomes?
plotData <- subset(chr_order_max, subset=list('mmusculus'=c('X')))
# by coordinates
bezier_chr <- buildBezierDF(plotData$homolog_df_list,
colname1='topChr_finalOffset',
colname2='bottomChr_finalOffset')
# by the index
bezier_ix <- buildBezierDF(plotData$homolog_df_list,
colname1='topIx_finalOffset',
colname2='bottomIx_finalOffset')
# by homolog chromosome names
bezier_mini <- buildBezierDF(plotData$homolog_df_list,
colname1='topMini_finalOffset',
colname2='bottomMini_finalOffset')Plot the data generated by each method to compare their results.
# by coordinates
orthoRibbonPlot(common_name, bezier_chr,
plotData$chrom_bars_df,
plotData$chrom_label_df)# # by the index
# orthoRibbonPlot(common_name, bezier_ix,
# plotData$chrom_bars_df,
# plotData$chrom_label_df)
# by homologous chromosome names
# the gene is not ordered by coordinates but ordered by the homologous chromosome names
## And Here we will show the symbols for each homologous.
symbol_df_top <- plotData$symbol_df_top
symbol_df_top <-
symbol_df_top[sample(seq.int(nrow(symbol_df_top)), 50), ] # random select 50 symbols
symbol_df_top$x <- symbol_df_top$xMini
symbol_df_bottom <- plotData$symbol_df_bottom
symbol_df_bottom <-
symbol_df_bottom[sample(seq.int(nrow(symbol_df_bottom)), 50), ] # random select 50 symbols
symbol_df_bottom$x <- symbol_df_bottom$xMini
orthoRibbonPlot(common_name, bezier_mini,
plotData$chrom_bars_df,
plotData$chrom_label_df,
show_symbol=TRUE,
symbol_df_top=symbol_df_top,
symbol_df_bottom=symbol_df_bottom,
symbol_size=3,
link_alpha=0.1) # add alpha value for the link## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 26.04.1 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## time zone: Etc/UTC
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats4 stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] GenomeInfoDb_1.49.1 Seqinfo_1.3.2
## [3] IRanges_2.47.5 S4Vectors_0.51.10
## [5] BiocGenerics_0.59.12 generics_0.1.4
## [7] geneClusterPattern_0.99.2 orthoRibbon_0.99.0
##
## loaded via a namespace (and not attached):
## [1] DBI_1.3.0 bitops_1.1-0
## [3] httr2_1.3.0 biomaRt_2.69.4
## [5] rlang_1.3.0 magrittr_2.0.5
## [7] otel_0.2.0 matrixStats_1.5.0
## [9] compiler_4.6.1 RSQLite_3.53.3
## [11] GenomicFeatures_1.65.0 png_0.1-9
## [13] vctrs_0.7.3 txdbmaker_1.9.0
## [15] stringr_1.6.0 pwalign_1.9.1
## [17] pkgconfig_2.0.3 crayon_1.5.3
## [19] fastmap_1.2.0 dbplyr_2.6.0
## [21] XVector_0.53.0 labeling_0.4.3
## [23] ca_0.72 trackViewer_1.49.3
## [25] Rsamtools_2.29.0 rmarkdown_2.32
## [27] UCSC.utils_1.9.0 strawr_0.0.92
## [29] bit_4.6.0 xfun_0.61
## [31] cachem_1.1.0 cigarillo_1.3.1
## [33] jsonlite_2.0.0 progress_1.2.3
## [35] blob_1.3.0 rhdf5filters_1.25.4
## [37] DelayedArray_0.39.8 tweenr_2.0.3
## [39] Rhdf5lib_2.1.0 BiocParallel_1.47.0
## [41] parallel_4.6.1 prettyunits_1.2.0
## [43] R6_2.6.1 bslib_0.12.0
## [45] stringi_1.8.9 RColorBrewer_1.1-3
## [47] rtracklayer_1.73.0 GenomicRanges_1.65.4
## [49] jquerylib_0.1.4 iterators_1.0.14
## [51] Rcpp_1.1.2 SummarizedExperiment_1.43.0
## [53] knitr_1.52 BiocBaseUtils_1.15.1
## [55] Matrix_1.7-6 tidyselect_1.2.1
## [57] abind_1.4-8 yaml_2.3.12
## [59] TSP_1.2.7 codetools_0.2-20
## [61] curl_8.0.0 lattice_0.23-1
## [63] tibble_3.3.1 withr_3.0.3
## [65] S7_0.2.2 InteractionSet_1.41.0
## [67] Biobase_2.73.2 KEGGREST_1.53.6
## [69] evaluate_1.0.5 polyclip_1.10-7
## [71] BiocFileCache_3.3.0 Biostrings_2.81.9
## [73] pillar_1.11.1 BiocManager_1.30.27
## [75] filelock_1.0.3 MatrixGenerics_1.25.0
## [77] foreach_1.5.2 RCurl_1.98-1.20
## [79] ggplot2_4.0.3 hms_1.1.4
## [81] scales_1.4.0 BiocStyle_2.41.0
## [83] grImport_0.9-7 glue_1.8.1
## [85] maketools_1.3.2 tools_4.6.1
## [87] BiocIO_1.23.3 sys_3.4.3
## [89] GenomicAlignments_1.49.2 registry_0.5-1
## [91] RANN_2.6.3 buildtools_1.0.0
## [93] XML_3.99-0.25 rhdf5_2.57.18
## [95] grid_4.6.1 seriation_1.5.8
## [97] AnnotationDbi_1.75.2 ggforce_0.5.0
## [99] restfulr_0.0.17 cli_3.6.6
## [101] S4Arrays_1.13.2 dplyr_1.2.1
## [103] gtable_0.3.6 sass_0.4.10
## [105] digest_0.6.39 ggrepel_0.9.8
## [107] SparseArray_1.13.4 rjson_0.2.23
## [109] htmlwidgets_1.6.4 farver_2.1.2
## [111] memoise_2.0.1 htmltools_0.5.9
## [113] lifecycle_1.0.5 httr_1.4.9
## [115] MASS_7.3-66 bit64_4.8.6