Introduction

In this document I provide a high-level graphical overview of the combined finances of the nonprofit Mozilla Foundation and the Mozilla Corporation, its wholly-owned subsidiary.

For those readers unfamiliar with the R statistical software and the additional Tidyverse software I use to manipulate and plot data, I’ve included some additional explanation of various steps. For more information check out the the tutorial “Getting started with the Tidyverse”.

Setup and data preparation

Libraries

I use the following R packages for the following purposes:

  • tidyverse: do general data manipulation.
library(tidyverse)

Data sources

I use data from the independent auditors’ reports and consolidated financial statements for the combined Mozilla organizations for 2005 (the year in which the Mozilla Corporation was founded) to 2018 (the latest report available at the time of writing). In particular I use data relating to activities and changes in net assets.

I extracted data from the consolidated financial statements into the following files:

  • mozilla-revenues.csv. CSV file of data from the financial statements relating to revenues coming in to the combined organizations from third parties.
  • mozilla-expenses.csv. CSV file of data from the financial statements relating to expenses incurred by the combined organizations.

The files do not contain any data relating to transfers between the organizations, for example, royalties paid to the Mozilla Foundation by the Mozilla Corporation for use of the Mozilla, Firefox, and other trademarks, or money paid by the Mozilla Foundation to the Mozilla Corporation for office space, etc. That data can be found in the IRS Form 990 documents filed by the Mozilla Foundation to the US Internal Revenue Service, as well as in the notes to the consolidated financial statements.

The file mozilla-revenues.csv contains multiple fields as shown below. All values are in USD. Positive contributions to revenue (gains) are stored as positive numbers, while negative contributions to revenue (losses) are stored as negative numbers.

  • revenue. Total revenue from all sources. Reported as “Total revenue and support” for 2005 to 2008 and 2018 and as “Total unrestricted revenue and support” for 2009 to 2017.
  • royalties. Royalties paid to the combined organizations by third parties. (This is not to be confused with royalties paid to the Mozilla Foundation by the Mozilla Corporation, which are considered inter-organizational transfers and are not reported on the consolidated financial statements.) Reported as “Royalties” beginning in 2008. For 2005 to 2007 this category was instead reported as two different categories, “Royalties - Search” and “Royalties - Product Sales”.
  • royalties_search. Royalties from search engine providers. Reported as “Royalties - search” for 2005 to 2007. Not reported separately beginning in 2008.
  • royalties_product. Royalties received in connection with product sales. Reported as “Royalties - product sales” for 2005 to 2007. Not reported separately beginning in 2008.
  • subs_ads. Revenue from subscriptions and advertising. Reported as “Subscription and advertising revenue“ beginning in 2017. Not reported separately for 2005 to 2016.
  • product. Revenue from products (as distinguished from product royalties). Reported as “Product revenues” for 2005 to 2007. Not reported separately beginning in 2008.
  • services. Revenue from services provided under contracts with third parties. Reported as “Contracted services” for 2005 to
    1. Not reported separately beginning in 2008.
  • interest. Interest and dividends received from third parties. Reported as “Interest income” for 2005 and 2006 and as “Interest and dividend income” beginning in 2007.
  • investments. Gains or losses resulting from investments made by either organization. Reported variously as “Unrealized losses from investments“ (2005), “Unrealized gain (loss) from investments” (2006), “Net realized and unrealized gain from investments” (2007, 2013–2014), “Net realized and unrealized (loss) gain from investments” (2008, 2011), “Net realized and unrealized loss from investments” (2009–2010), “Net realized and unrealized gain (loss) from investments“ (2012, 2015–2017), and “Net realized and unrealized (loss) gain on investments, net” (2018).
  • contributions. Contributions received from third parties, e.g., as grants or donations to the Mozilla Foundation. Reported as “Contributions” for 2005 and 2006 and for 2008 and subsequent years, and as “Contributions - Unrestricted” in 2007.
  • other. Income not included in any of the other categories. Reported as “Other income” for 2005 and 2006 and as “Other” beginning in 2015. Not reported separately for 2007 to 2014.
  • forex. Gains or losses from foreign currency exchange. Reported as “Foreign currency exchange (loss) gain” beginning in 2009, except for 2017 when it was reported as “Foreign currency exchange gain (loss)”. Not reported separately for 2005 to 2008.
  • assets. Losses resulting from the sale of assets. Reported as “Loss on sales of assets” beginning in 2009. Not reported separately for 2005 to 2008.
  • released. Assets released from previous restrictions and made available for use on an unrestricted basis. (For example, this apparently applies to certain grants made to the Mozilla Foundation by third parties.) Reported as “Net assets released from restrictions” beginning in 2009. Not reported separately in 2005, 2006, and 2008. The 2007 statements have a category for restricted contributions that is related to this.

The file mozilla-expenses.csv contains fields as shown below. All values are in USD. Expenses are stored as positive numbers, so must be subtracted from revenues to determine profits (or more correctly, net gains in assets, since the Mozilla Foundation does not have profits as such).

  • expenses. Total expenses for all categories. Reported as “Total expenses”.
  • software. Expenses related to software development. Reported as Software development.
  • programs. Expenses related to various projects and programs undertaken in support of the overall nonprofit goals of the combined organizations, including events, fellowships, sponsorships, and grants made to third parties. Reported as “Program services” for 2006 to 2017 and as “Other program services” in 2018.
  • depreciation_program. Depreciation expenses related to software development and other program services. Reported as “Depreciation” under “Program” for 2013 to 2017. Not reported separately for 2018.
  • marketing. Expenses related to sales, branding, and marketing. Reported as Sales and marketing for 2005 to 2007 and as “Branding and marketing” beginning in 2008.
  • general. Expenses related to running the organizations, including employee salaries and benefits, facilities, etc. Reported as “General and administrative”.
  • depreciation_support. Depreciation expenses related to marketing and general and administrative activities in support of the combined organizations. Reported as “Depreciation” under “Support” for 2013 to 2017. Not reported separately for 2018.
  • fundraising. Expenses related to raising funds, e.g., to solicit individual donations or grants from third-parties. Reported as “Fundraising and development” beginning in 2016.
  • depreciation_fundraising. Depreciation expenses related to fundraising. Reported as “Depreciation” under “Fundraising” for 2016 and 2017. Not reported separately for 2018.
  • other. Other expenses not allocated to any of the categories above. Reported as “Other expenses” for 2011 and 2012.

Reading in the data

I begin by reading in the two CSV files into the data frames revenues and expenses.

revenues <- read_csv("mozilla-revenues.csv")
## Parsed with column specification:
## cols(
##   year = col_double(),
##   revenue = col_double(),
##   royalties = col_double(),
##   royalties_search = col_double(),
##   royalties_product = col_double(),
##   subs_ads = col_double(),
##   product = col_double(),
##   services = col_double(),
##   interest = col_double(),
##   investments = col_double(),
##   contributions = col_double(),
##   other = col_double(),
##   forex = col_double(),
##   assets = col_double(),
##   released = col_double()
## )
expenses <- read_csv("mozilla-expenses.csv")
## Parsed with column specification:
## cols(
##   year = col_double(),
##   expenses = col_double(),
##   programs = col_double(),
##   software = col_double(),
##   depreciation_program = col_double(),
##   marketing = col_double(),
##   general = col_double(),
##   depreciation_support = col_double(),
##   fundraising = col_double(),
##   depreciation_fundraising = col_double(),
##   other = col_double()
## )

I then clean the data to make it easier to manipulate:

  • I need to treat royalties specially: prior to 2008 royalties from search engine providers were reported separately from other royalties, while in 2008 and subsequent years they were combined. For years in which the royalties data is missing I calculate royalties as the sum of the product and search royalties. I then remove the data for product and search royalties as no longer needed.
  • To make further calculations easier, I replace any remaining missing values with zero in both the revenue and expense tables.
revenues <- revenues %>%
  mutate(royalties = ifelse(is.na(royalties),
                            royalties_product + royalties_search,
                            royalties)) %>%
  select(-royalties_product, -royalties_search) %>%
  replace(is.na(.), 0)

expenses <- expenses %>%
  replace(is.na(.), 0)

Analysis

Mozilla revenue vs. expenses

I first create an overview showing overall revenues versus expenses for the years covered by the data.

I first create a new table r_vs_e by joining the revenues and expenses tables on their common field year, and then retaining only the year, revenue, and expenses fields.

r_vs_e <- inner_join(revenues, expenses, by = "year") %>%
  select(year, revenue, expenses)

I then plot the revenue and expenses data with year on the x-axis.

r_vs_e %>%
  mutate(revenue = revenue / 1000000, expenses = expenses / 1000000) %>%
  pivot_longer(revenue:expenses) %>%
  ggplot() +
  geom_line(
    mapping = aes(x = year, y = value, group = name, color = name),
    size = 0.8
  ) +
  ylab("") +
  xlab("Year") +
  scale_x_continuous(breaks = seq(2002, 2018, 2)) +
  scale_y_continuous(breaks = seq(0, 600, 100),
                     labels = scales::dollar_format(suffix = "M")) +
  scale_color_manual(values = c("#E69F00", "#56B4E9"),
                     breaks = c("revenue", "expenses"),
                     labels = c("Revenue", "Expenses")) +
  labs(title="Mozilla Revenue vs. Expenses",
       subtitle="Mozilla Foundation and Mozilla Corporation Combined",
       caption=paste0(
         "Data source:",
         "\n    Mozilla Foundation and Subsidiary Independent Auditors’ Report and Consolidated Financial Statements, 2005-2018")) +
  theme_minimal() +
  theme(axis.title.x=element_text(margin=margin(t=10))) +
  theme(axis.title.y=element_text(margin=margin(r=10))) +
  theme(plot.caption=element_text(margin=margin(t=15), hjust=0)) +
  theme(legend.title = element_blank())

Revenue categories

I next want to look at broad categories of revenue and how they have grown or shrunk over the years. To do this I categorize the various types of revenue as follows:

  • I keep royalties as a separate category, since it has been by far the major source of revenue over the years, with almost all of it coming from Google and other search engine providers.
  • I also keep subscription and ad revenue as a separate category, since it presumably will be where new sources of revenue such as the Mozilla VPN get reported.
  • I combine the contributions and released variables into a single variable grants_donations, based on my understanding that the released values primarily reflect grants to the Mozilla Foundation that have associated conditions placed on them, with the money not being released for unrestricted use until the conditions are satisfied.
  • I combine all other sources of revenue (or losses) into a single category misc_revenue.
revenue_categories <- revenues %>%
  mutate(grants_donations = contributions + released,
         misc_revenue = product + services + interest + investments + other + forex + assets) %>%
  select(year, revenue, royalties, subs_ads, grants_donations, misc_revenue)

I then plot the various categories of revenue for the years for which I have data.

revenue_category_palette <- c("#000000", "#E69F00", "#56B4E9",
                              "#009E73", "#CC79A7")

revenue_categories %>%
  mutate(across(revenue:misc_revenue, ~.x / 1000000)) %>%
  pivot_longer(revenue:misc_revenue) %>%
  ggplot() +
  geom_line(
    mapping = aes(x = year, y = value, group = name, color = name),
    size = 0.8
  ) +
  ylab("") +
  xlab("Year") +
  scale_x_continuous(breaks = seq(2002, 2018, 2)) +
  scale_y_continuous(breaks = seq(0, 600, 100),
                     labels = scales::dollar_format(suffix = "M")) +
  scale_color_manual(values = revenue_category_palette,
                     breaks = c("revenue", "royalties", "subs_ads",
                                "grants_donations", "misc_revenue"),
                     labels = c("Total Revenue",
                                "Royalties",
                                "Subscriptions and advertising",
                                "Grants and donations",
                                "Miscellaneous")) +
  labs(title="Mozilla Revenue Categories",
       subtitle="Mozilla Foundation and Mozilla Corporation Combined",
       caption=paste0(
         "Data source:",
         "\n    Mozilla Foundation and Subsidiary Independent Auditors’ Report and Consolidated Financial Statements, 2005-2018")) +
  theme_minimal() +
  theme(axis.title.x=element_text(margin=margin(t=10))) +
  theme(axis.title.y=element_text(margin=margin(r=10))) +
  theme(plot.caption=element_text(margin=margin(t=15), hjust=0)) +
  theme(legend.title = element_blank())

I then plot the revenue categories as a percentage of total revenue.

revenue_categories %>%
  mutate(across(revenue:misc_revenue, ~100 * .x / revenue)) %>%
  pivot_longer(revenue:misc_revenue) %>%
  ggplot() +
  geom_line(
    mapping = aes(x = year, y = value, group = name, color = name),
    size = 0.8
  ) +
  ylab("") +
  xlab("Year") +
  scale_x_continuous(breaks = seq(2002, 2018, 2)) +
  scale_y_continuous(breaks = seq(0, 100, 20),
                     labels = scales::percent_format(scale = 1)) +
  scale_color_manual(values = revenue_category_palette,
                     breaks = c("revenue", "royalties", "subs_ads",
                                "grants_donations", "misc_revenue"),
                     labels = c("Total Revenue",
                                "Royalties",
                                "Subscriptions and advertising",
                                "Grants and donations",
                                "Miscellaneous")) +
  labs(title="Mozilla Revenue Categories as a Percentage of Total Revenue",
       subtitle="Mozilla Foundation and Mozilla Corporation Combined",
       caption=paste0(
         "Data source:",
         "\n    Mozilla Foundation and Subsidiary Independent Auditors’ Report and Consolidated Financial Statements, 2005-2018")) +
  theme_minimal() +
  theme(axis.title.x=element_text(margin=margin(t=10))) +
  theme(axis.title.y=element_text(margin=margin(r=10))) +
  theme(plot.caption=element_text(margin=margin(t=15), hjust=0)) +
  theme(legend.title = element_blank())

Expense categories

I next want to look at broad categories of expenses and how they have grown or shrunk over the years. To do this I categorize the various types of expenses as follows:

  • I keep expenses related to software development (software) as a separate category, since it represents the main activity of the combined organizations and reflects value provided by Mozilla to the world at large in the form of open source software products.
  • I also keep program-related expenses (programs) as a separate category, since it represents another form of value provided by Mozilla to the world at large, in the form of grants, fellowships, sponsorships, events, and so on.
  • I retain marketing expenses, general and administrative expenses, and fundraising expensese as their own categories.
  • I combine all other expenses, including depreciation-related expenses, into a single category misc_expenses. (I wasn’t sure exactly what to do with depreciation expenses, since as reported they can’t be exactly assigned to the various other categories.)
expense_categories <- expenses %>%
  mutate(misc_expenses = depreciation_program + depreciation_support +
           depreciation_fundraising + other) %>%
  select(year, expenses, programs, software, marketing, general,
         fundraising, misc_expenses)

I then plot the various categories of expenses for the years for which I have data.

expense_category_palette <- c("#000000", "#E69F00", "#56B4E9", "#009E73",
                              "#0072B2", "#D55E00", "#CC79A7")

expense_categories %>%
  mutate(across(expenses:misc_expenses, ~.x / 1000000)) %>%
  pivot_longer(expenses:misc_expenses) %>%
  ggplot() +
  geom_line(
    mapping = aes(x = year, y = value, group = name, color = name),
    size = 0.8
  ) +
  ylab("") +
  xlab("Year") +
  scale_x_continuous(breaks = seq(2002, 2018, 2)) +
  scale_y_continuous(breaks = seq(0, 600, 100),
                     labels = scales::dollar_format(suffix = "M")) +
  scale_color_manual(values = expense_category_palette,
                     breaks = c("expenses", "software", "general",
                                "marketing", "programs", "fundraising",
                                "misc_expenses"),
                     labels = c("Total Expenses",
                                "Software development",
                                "General and administrative",
                                "Marketing",
                                "Programs",
                                "Fundraising",
                                "Miscellaneous Expenses")) +
  labs(title="Mozilla Expense Categories",
       subtitle="Mozilla Foundation and Mozilla Corporation Combined",
       caption=paste0(
         "Data source:",
         "\n    Mozilla Foundation and Subsidiary Independent Auditors’ Report and Consolidated Financial Statements, 2005-2018")) +
  theme_minimal() +
  theme(axis.title.x=element_text(margin=margin(t=10))) +
  theme(axis.title.y=element_text(margin=margin(r=10))) +
  theme(plot.caption=element_text(margin=margin(t=15), hjust=0)) +
  theme(legend.title = element_blank())

I then plot the expense categories as a percentage of total expenses:

expense_categories %>%
  mutate(across(expenses:misc_expenses, ~100 * .x / expenses)) %>%
  pivot_longer(expenses:misc_expenses) %>%
  ggplot() +
  geom_line(
    mapping = aes(x = year, y = value, group = name, color = name),
    size = 0.8
  ) +
  ylab("") +
  xlab("Year") +
  scale_x_continuous(breaks = seq(2002, 2018, 2)) +
  scale_y_continuous(breaks = seq(0, 100, 20),
                     labels = scales::percent_format(scale = 1)) +
  scale_color_manual(values = expense_category_palette,
                     breaks = c("expenses", "software", "general",
                                "marketing", "programs", "fundraising",
                                "misc_expenses"),
                     labels = c("Total Expenses",
                                "Software development",
                                "General and administrative",
                                "Marketing",
                                "Programs",
                                "Fundraising",
                                "Miscellaneous Expenses")) +
  labs(title="Mozilla Expense Categories as a Percentage of Total Expenses",
       subtitle="Mozilla Foundation and Mozilla Corporation Combined",
       caption=paste0(
         "Data source:",
         "\n    Mozilla Foundation and Subsidiary Independent Auditors’ Report and Consolidated Financial Statements, 2005-2018")) +
  theme_minimal() +
  theme(axis.title.x=element_text(margin=margin(t=10))) +
  theme(axis.title.y=element_text(margin=margin(r=10))) +
  theme(plot.caption=element_text(margin=margin(t=15), hjust=0)) +
  theme(legend.title = element_blank())

Appendix

Caveats

I am not an expert in accounting, so I may have made various mistakes in my interpretation of the financial statements, as well as in the way I combined multiple revenue and expense items in broader categories.

To do

Here are some other possible tasks for anyone who’d like to add to this article:

  • Extend the data set and graphs to 2019 when Mozilla releases its 2019 financial results. This should be a relatively simple task if the 2019 results use the same categories as in 2018.
  • Extend the data set to 2003 and 2004, using data from the Mozilla Foundation IRS Form 990 documents for those years. This will require a bit of work to find the Form 990 equivalents to the various revenue and expense categories in the consolidated financial statements.
  • Do a more in-depth analysis of the expense categories, using the information contained in the notes to the consolidated financial statements. This will require a fair amount of work to track down all the items and match them up with the higher-level categories in the “Consolidated Statement of Activities and Change in Net Assets” section of the statements.
  • Do an analysis of the assets and liabilities and cash flows sections of the consolidated financial statements. This will require some knowledge of accounting and how to read financial statements (more than I possess, at least).

References

Mozilla financial statements and Mozilla Foundation IRS Form 990 documents are linked to from the Mozilla Foundation public records page.

Environment

I used the following R environment in doing the analysis above:

sessionInfo()
## R version 4.0.2 (2020-06-22)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Catalina 10.15.6
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] forcats_0.5.0   stringr_1.4.0   dplyr_1.0.0     purrr_0.3.4    
## [5] readr_1.3.1     tidyr_1.1.0     tibble_3.0.3    ggplot2_3.3.2  
## [9] tidyverse_1.3.0
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.5       cellranger_1.1.0 pillar_1.4.6     compiler_4.0.2  
##  [5] dbplyr_1.4.4     tools_4.0.2      digest_0.6.25    lubridate_1.7.9 
##  [9] jsonlite_1.7.0   evaluate_0.14    lifecycle_0.2.0  gtable_0.3.0    
## [13] pkgconfig_2.0.3  rlang_0.4.7      reprex_0.3.0     cli_2.0.2       
## [17] rstudioapi_0.11  DBI_1.1.0        yaml_2.2.1       haven_2.3.1     
## [21] xfun_0.16        withr_2.2.0      xml2_1.3.2       httr_1.4.2      
## [25] knitr_1.29       fs_1.4.2         hms_0.5.3        generics_0.0.2  
## [29] vctrs_0.3.2      grid_4.0.2       tidyselect_1.1.0 glue_1.4.1      
## [33] R6_2.4.1         fansi_0.4.1      readxl_1.3.1     rmarkdown_2.3   
## [37] farver_2.0.3     modelr_0.1.8     blob_1.2.1       magrittr_1.5    
## [41] backports_1.1.8  scales_1.1.1     ellipsis_0.3.1   htmltools_0.5.0 
## [45] rvest_0.3.6      assertthat_0.2.1 colorspace_1.4-1 stringi_1.4.6   
## [49] munsell_0.5.0    broom_0.7.0      crayon_1.3.4

Source code

You can find the source code and data for this analysis at my mozilla-finances public code repository. This document and its associated source code and data are available for unrestricted use, distribution and modification under the terms of the Creative Commons CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. Stated more simply, you’re free to do whatever you’d like with it.