diff --git a/R/facet.R b/R/facet.R
index 5617a3c1..744360d3 100644
--- a/R/facet.R
+++ b/R/facet.R
@@ -453,6 +453,10 @@ draw_facet_window = function(grid, ...) {
           grid
         }
       }
+      
+      # drawn elements
+      if (!is.null(draw)) eval(draw)
+      
     } # end of ii facet loop
   } # end of add check
 
diff --git a/R/tinyplot.R b/R/tinyplot.R
index c2fc2e6a..0b36ae49 100644
--- a/R/tinyplot.R
+++ b/R/tinyplot.R
@@ -253,6 +253,13 @@
 #'   added elements will be turned off. See also [tinyplot_add], which provides
 #'   a convenient wrapper around this functionality for layering on top of an
 #'   existing plot without having to repeat arguments.
+#' @param draw a function that draws directly on the plot canvas (before `x` and
+#'   `y` are plotted). The `draw` argument is primarily useful for adding common
+#'   elements to each facet of a faceted plot, e.g.
+#'   \code{\link[graphics]{abline}} or \code{\link[graphics]{text}}. Note that
+#'   this argument is somewhat experimental and that _no_ internal checking is
+#'   done for correctness; the provided argument is simply captured and
+#'   evaluated as-is. See Examples.
 #' @param flip logical. Should the plot orientation be flipped, so that the
 #'   y-axis is on the horizontal plane and the x-axis is on the vertical plane?
 #'   Default is FALSE.
@@ -433,6 +440,15 @@
 #'  data = aq
 #' )
 #' 
+#' # To add common elements to each facet, use the `draw` argument
+#' 
+#' tinyplot(
+#'  Temp ~ Day,
+#'  facet = windy ~ hot,
+#'  data = aq,
+#'  draw = abline(h = 75, lty = 2, col = "hotpink")
+#' )
+#' 
 #' # The (automatic) legend position and look can be customized using
 #' # appropriate arguments. Note the trailing "!" in the `legend` position
 #' # argument below. This tells `tinyplot` to place the legend _outside_ the plot
@@ -520,6 +536,7 @@ tinyplot.default = function(
     ymin = NULL,
     ymax = NULL,
     add = FALSE,
+    draw = NULL,
     file = NULL,
     width = NULL,
     height = NULL,
@@ -543,6 +560,7 @@ tinyplot.default = function(
   dots = list(...)
 
   if (isTRUE(add)) legend = FALSE
+  draw = substitute(draw)
   
 
   # sanitize arguments
@@ -1001,7 +1019,9 @@ tinyplot.default = function(
     ymax = datapoints$ymax, ymin = datapoints$ymin,
     xaxt = xaxt, xlabs = xlabs, xlim = xlim,
     yaxt = yaxt, ylabs = ylabs, ylim = ylim,
-    flip = flip, xaxs = xaxs, yaxs = yaxs
+    flip = flip,
+    draw = draw,
+    xaxs = xaxs, yaxs = yaxs
   )
   list2env(facet_window_args, environment())
 
diff --git a/man/tinyplot.Rd b/man/tinyplot.Rd
index 72e52c28..45f45e9e 100644
--- a/man/tinyplot.Rd
+++ b/man/tinyplot.Rd
@@ -46,6 +46,7 @@ tinyplot(x, ...)
   ymin = NULL,
   ymax = NULL,
   add = FALSE,
+  draw = NULL,
   file = NULL,
   width = NULL,
   height = NULL,
@@ -394,6 +395,14 @@ added elements will be turned off. See also \link{tinyplot_add}, which provides
 a convenient wrapper around this functionality for layering on top of an
 existing plot without having to repeat arguments.}
 
+\item{draw}{a function that draws directly on the plot canvas (before \code{x} and
+\code{y} are plotted). The \code{draw} argument is primarily useful for adding common
+elements to each facet of a faceted plot, e.g.
+\code{\link[graphics]{abline}} or \code{\link[graphics]{text}}. Note that
+this argument is somewhat experimental and that \emph{no} internal checking is
+done for correctness; the provided argument is simply captured and
+evaluated as-is. See Examples.}
+
 \item{file}{character string giving the file path for writing a plot to disk.
 If specified, the plot will not be displayed interactively, but rather sent
 to the appropriate external graphics device (i.e.,
@@ -599,6 +608,15 @@ tinyplot(
  data = aq
 )
 
+# To add common elements to each facet, use the `draw` argument
+
+tinyplot(
+ Temp ~ Day,
+ facet = windy ~ hot,
+ data = aq,
+ draw = abline(h = 75, lty = 2, col = "hotpink")
+)
+
 # The (automatic) legend position and look can be customized using
 # appropriate arguments. Note the trailing "!" in the `legend` position
 # argument below. This tells `tinyplot` to place the legend _outside_ the plot
diff --git a/vignettes/introduction.qmd b/vignettes/introduction.qmd
index 65d7d81f..9e263550 100644
--- a/vignettes/introduction.qmd
+++ b/vignettes/introduction.qmd
@@ -32,8 +32,12 @@ of the `airquality` dataset that comes bundled with base R.
 ```{r aq}
 library(tinyplot)
 
-aq = airquality
-aq$Month = factor(month.abb[aq$Month], levels = month.abb[5:9])
+aq = transform(
+  airquality,
+  Month = factor(month.abb[Month], levels = month.abb[5:9]),
+  hot   = ifelse(Temp>=75, "hot", "cold"),
+  windy = ifelse(Wind>=15, "windy", "calm")
+)
 ```
 
 ## Equivalence with `plot()`
@@ -374,51 +378,46 @@ tinyplot(
 )
 ```
 
-By default, facets will be arranged in a square configuration if more than
-three facets are detected. Users can override this behaviour by supplying
-`nrow` or `ncol` in the "facet.args" helper function. (The margin padding
-between individual facets can also be adjusted via the `fmar` argument.) Note
-that we can also reduce axis label redundancy by turning off the plot frame.
+Facets are easily combined with grouping. This can either be done separately
+(i.e., distinct arguments for `by` and `facet`), or along the same dimension.
+For the latter case, we provide a special `facet = "by"` convenience shorthand.
 
-```{r facet_nrow}
+```{r facet_by}
 tinyplot(
-  Temp ~ Day, aq,
-  facet = ~Month, facet.args = list(nrow = 1),
+  Temp ~ Day | Month, aq,
+  facet = "by", # facet along same dimension as groups
   type = "lm",
   grid = TRUE,
-  frame = FALSE,
   main = "Predicted air temperatures"
 )
 ```
 
-Here's a slightly fancier version where we combine facets with (by) colour
-grouping, add a background fill to the facet text, and also overlay the
-original values alongside our model predictions. For this particular example,
-we'll use the `facet = "by"` convenience shorthand to facet along the same
-month variable as the colour grouping. But you can easily specify different `by`
-and `facet` variables if that's what your data support.
+To customize facets, simply pass a list of named arguments through the companion
+`facet.args` argument. For example, users can override the default "square"
+facet window arrangement by supplying explicit `nrow` or `ncol` values. They can
+also adjust the padding (margin) between individual facets, change the facet
+title text and background, etc., etc. Here's a simple example where we (1)
+arrange the facets in a single row, (2) add some background fill to the facet
+text, and (3) and reduce axis redundancy by turning off the plot frame.
 
-```{r facet_fancy}
+```{r facet_nrow}
 tinyplot(
-  Temp ~ Day | Month, aq,
-  facet = "by", facet.args = list(bg = "grey90"),
+  Temp ~ Day, aq,
+  facet = ~Month, facet.args = list(nrow = 1, bg = "grey90"),
   type = "lm",
-  palette = "dark2",
   grid = TRUE,
-  axes = "l",
-  ylim = c(50, 100),
-  main = "Actual and predicted air temperatures"
+  frame = FALSE, # turning off the plot frame means only outer axes will be printed
+  main = "Predicted air temperatures"
 )
-tinyplot_add(type = "p")
 ```
 
-Note that the `facet` argument also accepts a _two-sided_ formula for arranging
+The `facet.args` customizations can also be set globally via the `tpar()`
+function. We will revisit this idea in the @themes section below.
+
+Finally, the `facet` argument also accepts a _two-sided_ formula for arranging
 facets in a fixed grid layout. Here's a simple (if contrived) example.
 
 ```{r facet_grid}
-aq$hot = ifelse(aq$Temp>=75, "hot", "cold")
-aq$windy = ifelse(aq$Wind>=15, "windy", "calm")
-
 tinyplot(
  Temp ~ Day, data = aq,
  facet = windy ~ hot,
@@ -430,28 +429,89 @@ tinyplot(
 )
 ```
 
-The `facet.args` customizations can also be set globally via the `tpar()`
-function, which provides a nice segue to our penultimate section.
-
 ## Layers
 
-In many contexts, it is convenient to build plots step-by-step, adding layers with different elements on top of a base. 
-
-The `tinyplot()` function includes an `add=TRUE` argument that adds element to an existing plot, instead of drawing a new window. This argument is useful, but a bit verbose, as it requires users to make very similar successive calls, with many shared arguments.
-
-For convenience, the package also includes a `tinyplot_add()` function (alias `plt_add()`), which captures the last `tinyplot()` call, keeps all the same arguments, and modifies just the arguments that the user explicitly wants to change. 
-
-In the following example, we first draw linear regression lines with facets and group coloring. Then, we add the original data points using `tinyplot_add()`. Notice that the same grouping and data options are carried over to points, without having to specify them in the `tinyplot_add()` function.
+In many contexts, it is convenient to build plots step-by-step, adding layers
+with different elements on top of a base. The **tinyplot** package offers a few
+ways to achieve this layering effect.
+
+Similar to many base plotting functions, users can invoke the
+`tinyplot(..., add=TRUE)` argument to draw a plot on top of an existing one,
+rather than opening a new window. However, while this argument is useful, it can
+become verbose since it requires users to make very similar successive calls,
+with many shared arguments.
+
+For this reason, the **tinyplot** package also provides a special
+`tinyplot_add()` (alias `plt_add()`) convenience function for adding layers to
+an existing tinyplot. The idea is that users need simply pass the _specific_
+arguments that they want to add or modify relative to the base layer, and all
+arguments will be inherited from the original.
+
+An example may help to demonstrate. Here we first draw some faceted points with
+group colouring and various other aesthetic tweaks. Next, we add regression
+lines with a simple `tinyplot_add(type = "lm")` call. Notice that the original
+grouping, data and aesthetic options are all carried over correctly, without
+having to repeat ourselves.
+
+```{r tinyplot_add}
+tinyplot(
+  Temp ~ Day | Month, aq,
+  facet = "by", facet.args = list(bg = "grey90"),
+  palette = "dark2",
+  legend = FALSE,
+  grid = TRUE,
+  axes = "l",
+  ylim = c(50, 100),
+  main = "Actual and predicted air temperatures"
+)
+# Add regression lines
+tinyplot_add(type = "lm")
+```
 
-```{r}
-library(tinyplot)
+A related---but distinct---concept to adding plot layers is _drawing_ on a plot.
+The canonical use case is annotating your plot with text or some other
+function-based (rather than data-based) logic. For example, you may want to
+demarcate some threshold values with horizontal or vertical lines, or simply
+annotate your plot with text. The **tinyplot** way to do this is by passing the
+`tinyplot(..., draw = <draw_function>)` argument. Here we demonstrate with a
+simplified version of our facet grid example from earlier.
 
-tinyplot(Sepal.Width ~ Sepal.Length | Species,
-         facet = ~Species,
-         data = iris, 
-         type = "p")
+```{r draw_simple}
+tinyplot(
+ Temp ~ Day, data = aq,
+ facet = windy ~ hot,
+ # draw a horizontal (threshold) line in each facet using the abline function
+ draw = abline(h = 75, lty = 2, col = "hotpink")
+)
+```
 
-tinyplot_add(type = type_lm())
+Compared to "manually" drawing these elements on a plot _ex post_---e.g., via a
+separate `abline()` call---there are several advantages to the idiomatic
+**tinyplot** interface. First, the `draw` argument is facet-aware and will
+ensure that each individual facet is correctly drawn upon. Second, you can
+leverage the special `ii` internal counter to draw programmatically across
+facets (see
+[here](https://github.com/grantmcdermott/tinyplot/pull/245#issue-2642589951)).
+Third, the `draw` argument is fully generic and accepts _any_
+drawing/annotating function. You can combine multiple drawing functions by
+wrapping them with curly brackets`{}`, and even pass `tinyplot()` back towards
+itself.
+
+Here's a slightly more complicated "spaghetti" plot example, where we pass
+multiple functions through `draw = {...}`, including drawing all of the
+lines in the background (of each facet) via a secondary `tinyplot()` call.
+
+```{r draw_spaghetti}
+tinyplot(
+  Temp ~ Day | Month, aq, facet = "by", lwd = 3, type = "l",
+  frame = FALSE, legend = FALSE, ylim = c(50, 100),
+  draw = {
+    tinyplot(Temp ~ Day | Month, aq, col = "grey", type = "l", add = TRUE)
+    abline(h = 75, lty = 2)
+    text(5.5, 75, "Cold", pos = 1, offset = 0.4)
+    text(5.5, 75, "Hot", pos = 3, offset = 0.4)
+  }
+)
 ```
 
 ## Themes
