diff --git a/templates/zerver/for/open-source.md b/templates/zerver/for/open-source.md
index c4c2597e28..cfc7562213 100644
--- a/templates/zerver/for/open-source.md
+++ b/templates/zerver/for/open-source.md
@@ -126,11 +126,16 @@ Import your existing organization from [Slack](/help/import-from-slack),
[Mattermost](/help/import-from-mattermost), or
[Gitter](/help/import-from-gitter).
-### Syntax highlighting.
+### Collaborate on code and formulas
-[Full Markdown support](/help/format-your-message-using-markdown), including
-syntax highlighting, makes it easy to discuss code, paste an error message,
-or explain a complicated point. Full LaTeX support as well.
+[Markdown code blocks](/help/format-your-message-using-markdown#code)
+with syntax highlighting make it easy to discuss code, paste an error
+message, or explain a complicated point. Native LaTeX support provides
+the same benefits when talking about math.
+
+You can also instantly copy a code block to your clipboard or transfer
+it to an [external code playground](/help/add-a-custom-playground) to
+interactively run and debug the code.
If your community primarily uses a single programming language,
consider setting a default language for syntax highlighting.
diff --git a/templates/zerver/help/add-a-custom-playground.md b/templates/zerver/help/add-a-custom-playground.md
new file mode 100644
index 0000000000..eb60e72ed7
--- /dev/null
+++ b/templates/zerver/help/add-a-custom-playground.md
@@ -0,0 +1,72 @@
+# Add a custom playground
+
+{!admin-only.md!}
+
+Code playgrounds allow users to open the contents of a [code
+block][code-block] in an external playground of your choice by
+clicking a widget that appears when hovering over the code block.
+
+Code playgrounds can be configured for any programming language (the
+language of a code block is determined by the logic for syntax
+highlighting). You can also use a custom language name to implement
+simple integrations. For example, a playground for the language
+`send_tweet` could be used with a "playground" that sends the content
+of the code block as a Tweet.
+
+[code-block]: /help/format-your-message-using-markdown#code
+
+### Add a custom playground
+
+{start_tabs}
+
+{settings_tab|playground-settings}
+
+1. Under **Add a new playground**, enter a **Name**, **Language** and
+**URL prefix**.
+
+1. Click **Add playground**.
+
+{end_tabs}
+
+## Walkthrough with an example
+
+Consider the following example.
+
+* Name: `Rust playground`
+* Language: `Rust`
+* URL prefix: `https://play.rust-lang.org/?edition=2018&code=`
+
+When composing a message `rust` can be mentioned as the syntax highlighting
+language in the code blocks.
+
+~~~
+``` rust
+fn main() {
+ // A hello world Rust program for demo purposes
+ println!("Hello World!");
+}
+```
+~~~
+
+The user would then get a on-hover option to open the above code in the playground
+they had previously configured.
+
+## Technical details
+
+* You can configure multiple playgrounds for a given language; if you do that,
+ the user will get to choose which playground to open the code in.
+* The `Language` field is the human-readable Pygments language name for that
+ programming language. The syntax highlighting language in a code block
+ is internally mapped to these human-readable Pygments names.
+ E.g: `py3` and `py` are mapped to `Python`. We are working on implementing a
+ typeahead for looking up the Pygments name. Until then, one can use
+ [this Pygments method](https://pygments-doc.readthedocs.io/en/latest/lexers/lexers.html#pygments.lexers.get_lexer_by_name).
+* The links for opening code playgrounds are always constructed by
+ concatenating the provided URL prefix with the URL-encoded contents
+ of the code block.
+* Code playground sites do not always clearly document their URL
+ format; often you can just get the prefix from your browser's URL bar.
+
+If you have any trouble setting in setting up a code playground, please [contact
+us](/help/contact-support) with details on what you're trying to do, and we'll be
+happy to help you out.
diff --git a/templates/zerver/help/format-your-message-using-markdown.md b/templates/zerver/help/format-your-message-using-markdown.md
index 4db72fbd0b..23e0306539 100644
--- a/templates/zerver/help/format-your-message-using-markdown.md
+++ b/templates/zerver/help/format-your-message-using-markdown.md
@@ -118,6 +118,10 @@ Organization administrators can also configure a default syntax
highlighting language. In this configuration, one can use ````text`
to display content without any syntax highlighting.
+Hovering over a Zulip code block reveals buttons to copy the code or
+open it in one of the organization's configured [custom code
+playgrounds](/help/add-a-custom-playground).
+
## LaTeX
~~~
Inline: $$O(n^2)$$
diff --git a/templates/zerver/help/include/sidebar_index.md b/templates/zerver/help/include/sidebar_index.md
index 8d48b3622d..b59b36b6b8 100644
--- a/templates/zerver/help/include/sidebar_index.md
+++ b/templates/zerver/help/include/sidebar_index.md
@@ -140,6 +140,7 @@
* [Add custom emoji](/help/add-custom-emoji)
* [Configure authentication methods](/help/configure-authentication-methods)
* [Add a custom linkifier](/help/add-a-custom-linkifier)
+* [Add a custom playground](/help/add-a-custom-playground)
* [Message retention policy](/help/message-retention-policy)
* [SAML authentication](/help/saml-authentication)
diff --git a/zerver/lib/markdown/help_settings_links.py b/zerver/lib/markdown/help_settings_links.py
index 82958bbb0e..2c0f748c8d 100644
--- a/zerver/lib/markdown/help_settings_links.py
+++ b/zerver/lib/markdown/help_settings_links.py
@@ -62,6 +62,11 @@ link_mapping = {
"Linkifiers",
"/#organization/linkifier-settings",
],
+ "playground-settings": [
+ "Manage organization",
+ "Playgrounds",
+ "/#organization/playground-settings",
+ ],
"profile-field-settings": [
"Manage organization",
"Custom profile fields",
diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml
index 254f0aed5b..3c1a5fbc76 100644
--- a/zerver/openapi/zulip.yaml
+++ b/zerver/openapi/zulip.yaml
@@ -2629,7 +2629,8 @@ paths:
additionalProperties: false
description: |
Event sent to all users in a Zulip organization when the
- set of configured playgrounds for the organization has changed.
+ set of configured [code playgrounds](/help/add-a-custom-playground)
+ for the organization has changed.
**Changes**: New in Zulip 4.0 (feature level 49).
properties:
@@ -6985,8 +6986,7 @@ paths:
operationId: add_realm_playground
tags: ["server_and_organizations"]
description: |
- Configure realm playgrounds options to run code snippets occurring
- in a code block using playgrounds which supports that language.
+ Configure [code playgrounds](/help/add-a-custom-playground) for the organization.
`POST {{ api_url }}/v1/realm/playgrounds`
@@ -7042,8 +7042,8 @@ paths:
operationId: remove_realm_playground
tags: ["server_and_organizations"]
description: |
- Remove realm playground options to run code snippets in
- custom playgrounds
+ Remove a [code playground](/help/add-a-custom-playground) previously
+ configured for an organization.
`DELETE {{ api_url }}/v1/realm/playgrounds/{playground_id}`
@@ -7449,8 +7449,8 @@ paths:
description: |
Present if `realm_playgrounds` is present in `fetch_event_types`.
- An array of dictionaries where each dictionary describes a playground entry
- in this Zulip organization.
+ An array of dictionaries where each dictionary describes a
+ [code playground](/help/add-a-custom-playground) configured for this Zulip organization.
**Changes**: New in Zulip 4.0 (feature level 49).
realm_user_groups: