reglScatterplot is a thin R wrapper around the
JavaScript regl-scatterplot
library, exposing it as an htmlwidgets widget. It renders
millions of two-dimensional points in the browser via
WebGL, which makes it well suited to:
(x, y) scatter that ggplot2 would draw too
slowly.The widget works in:
htmlwidgets::saveWidget(),IRkernel
(e.g. VSCode’s Jupyter extension).The simplest call needs only a data.frame and the names
of the two coordinate columns.
Pass a character or factor column name (or vector) to
colorBy. A draggable legend appears in the upper-right
corner of the plot; clicking a legend entry filters the data, and
shift/ctrl-click extends the selection.
Numeric colorBy produces a continuous colour scale. Use
vmin/vmax to clip outliers; the strings
"p1", "p99" and so on select percentiles.
reglScatterplot(df,
x = "x", y = "y", colorBy = "score",
continuousPalette = "magma",
vmin = "p1", vmax = "p99"
)For log-fold-change-style data use centerZero = TRUE to
force a symmetric diverging scale.
reglScatterplot() works directly on a
SingleCellExperiment. Pass the name of a
reducedDim() as x. colorBy and
groupBy can name a colData() column or a gene
in rownames(). Gene values are read from the
logcounts assay by default; use assay to pick
another one.
Here we wrap the bundled example data in a small
SingleCellExperiment.
library(SingleCellExperiment)
#> Loading required package: SummarizedExperiment
#> Loading required package: MatrixGenerics
#> Loading required package: matrixStats
#>
#> Attaching package: 'MatrixGenerics'
#> The following objects are masked from 'package:matrixStats':
#>
#> colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
#> colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
#> colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
#> colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
#> colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
#> colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
#> colWeightedMeans, colWeightedMedians, colWeightedSds,
#> colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
#> rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
#> rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
#> rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
#> rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
#> rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
#> rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
#> rowWeightedSds, rowWeightedVars
#> Loading required package: GenomicRanges
#> Loading required package: stats4
#> Loading required package: BiocGenerics
#> Loading required package: generics
#>
#> Attaching package: 'generics'
#> The following objects are masked from 'package:base':
#>
#> as.difftime, as.factor, as.ordered, intersect, is.element, setdiff,
#> setequal, union
#>
#> Attaching package: 'BiocGenerics'
#> The following objects are masked from 'package:stats':
#>
#> IQR, mad, sd, var, xtabs
#> The following object is masked from 'package:utils':
#>
#> data
#> The following objects are masked from 'package:base':
#>
#> anyDuplicated, aperm, append, as.data.frame, basename, cbind,
#> colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
#> get, grep, grepl, is.unsorted, lapply, Map, mapply, match, mget,
#> order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank,
#> rbind, Reduce, rownames, sapply, saveRDS, scale, sequence, table,
#> tapply, transform, unique, unsplit, which.max, which.min
#> Loading required package: S4Vectors
#>
#> Attaching package: 'S4Vectors'
#> The following object is masked from 'package:utils':
#>
#> findMatches
#> The following objects are masked from 'package:base':
#>
#> expand.grid, I, unname
#> Loading required package: IRanges
#> Loading required package: Seqinfo
#> Loading required package: Biobase
#> Welcome to Bioconductor
#>
#> Vignettes contain introductory material; view with
#> 'browseVignettes()'. To cite Bioconductor, see
#> 'citation("Biobase")', and for packages 'citation("pkgname")'.
#>
#> Attaching package: 'Biobase'
#> The following object is masked from 'package:MatrixGenerics':
#>
#> rowMedians
#> The following objects are masked from 'package:matrixStats':
#>
#> anyMissing, rowMedians
data(reglScatterExample)
ex <- reglScatterExample
sce <- SingleCellExperiment(
assays = list(logcounts = t(as.matrix(ex["CD3D"]))),
colData = ex[, c("celltype", "nCount", "percentMito")],
reducedDims = list(UMAP = as.matrix(ex[, c("UMAP_1", "UMAP_2")]))
)
sce
#> class: SingleCellExperiment
#> dim: 1 3400
#> metadata(0):
#> assays(1): logcounts
#> rownames(1): CD3D
#> rowData names(0):
#> colnames: NULL
#> colData names(3): celltype nCount percentMito
#> reducedDimNames(1): UMAP
#> mainExpName: NULL
#> altExpNames(0):
reglScatterplot(sce, x = "UMAP", colorBy = "celltype")Colouring by a gene reads its row from the assay:
A SpatialExperiment works the same way. Use
x = "spatial" to plot the tissue coordinates from
spatialCoords():
Seurat objects and in-memory AnnData
objects (from anndataR) are also accepted. They need the
optional SeuratObject and anndataR
packages.
When several reglScatterplot() widgets share the same
plotIds, you can synchronise pan/zoom across them with
enableReglScatterplotSync():
library(shiny)
ui <- fluidPage(
fluidRow(
column(6, reglScatterplotOutput("plotA")),
column(6, reglScatterplotOutput("plotB"))
),
actionButton("link", "Link cameras")
)
server <- function(input, output, session) {
output$plotA <- renderReglScatterplot({
reglScatterplot(df,
x = "x", y = "y", colorBy = "cluster",
plotId = "plotA"
)
})
output$plotB <- renderReglScatterplot({
reglScatterplot(df,
x = "x", y = "y", colorBy = "score",
plotId = "plotB"
)
})
observeEvent(input$link, {
enableReglScatterplotSync(c("plotA", "plotB"))
})
}
shinyApp(ui, server)On first use the JavaScript bundles (d3,
regl-scatterplot, pickr,
html2canvas, jsPDF) are loaded from a CDN
(esm.sh and cdnjs.cloudflare.com). Once loaded
they are cached by the browser. A helpful error message is displayed
in-place if the network is unavailable.
sessionInfo()
#> 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] SingleCellExperiment_1.35.2 SummarizedExperiment_1.43.0
#> [3] Biobase_2.73.2 GenomicRanges_1.65.4
#> [5] Seqinfo_1.3.2 IRanges_2.47.5
#> [7] S4Vectors_0.51.10 BiocGenerics_0.59.12
#> [9] generics_0.1.4 MatrixGenerics_1.25.0
#> [11] matrixStats_1.5.0 reglScatterplotR_0.99.6
#>
#> loaded via a namespace (and not attached):
#> [1] sass_0.4.10 SparseArray_1.13.4 lattice_0.23-1
#> [4] digest_0.6.39 magrittr_2.0.5 evaluate_1.0.5
#> [7] grid_4.6.1 RColorBrewer_1.1-3 fastmap_1.2.0
#> [10] jsonlite_2.0.0 Matrix_1.7-6 promises_1.5.0
#> [13] viridisLite_0.4.3 jquerylib_0.1.4 abind_1.4-8
#> [16] cli_3.6.6 shiny_1.14.0 rlang_1.3.0
#> [19] XVector_0.53.0 cachem_1.1.0 DelayedArray_0.39.8
#> [22] yaml_2.3.12 otel_0.2.0 S4Arrays_1.13.2
#> [25] tools_4.6.1 httpuv_1.6.17 buildtools_1.0.0
#> [28] R6_2.6.1 mime_0.13 lifecycle_1.0.5
#> [31] htmlwidgets_1.6.4 bslib_0.12.0 later_1.4.8
#> [34] Rcpp_1.1.2 xfun_0.62 sys_3.4.3
#> [37] knitr_1.52 xtable_1.8-8 htmltools_0.5.9
#> [40] rmarkdown_2.32 maketools_1.3.2 compiler_4.6.1