惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
J
Java Code Geeks
G
Google Developers Blog
美团技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
The Blog of Author Tim Ferriss
月光博客
月光博客
B
Blog
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
博客园_首页
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
Y
Y Combinator Blog
Jina AI
Jina AI
S
SegmentFault 最新的问题
H
Help Net Security
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
Google DeepMind News
Google DeepMind News

MDN Blog

Introducing the MDN MCP server | MDN Blog Under the hood of MDN's new frontend | MDN Blog Image formats: Codecs and compression tools | MDN Blog A beginner-friendly guide to view transitions in CSS | MDN Blog Launching MDN's new front end | MDN Blog Image formats: Pixel data from encoders to decoders | MDN Blog Celebrating 20 years of MDN | MDN Blog Image formats: Color models for humans and devices | MDN Blog Default styles for h1 elements are changing | MDN Blog Implications of Global Privacy Control | MDN Blog JavaScript Temporal is coming | MDN Blog Fix your website's Largest Contentful Paint by optimizing image loading | MDN Blog MDN 2024 content projects | MDN Blog A new learning experience on MDN | MDN Blog Countdown to the holidays with daily coding challenges | MDN Blog Monitoring and optimizing website performance | MDN Blog How to land your first developer job | MDN Blog Introducing the new MDN Community page | MDN Blog Fixing your website's JavaScript performance | MDN Blog Get back to school! Supercharge your learning with MDN and Scrimba | MDN Blog Efficient data handling with the Streams API | MDN Blog Locale-sensitive text segmentation in JavaScript with Intl.Segmenter | MDN Blog Optimize your workflow with Git stash | MDN Blog How to debug mobile apps across devices | MDN Blog Exclusive accordions using the HTML details element | MDN Blog Exploring the Broadcast Channel API for cross-tab communication | MDN Blog MDN partners with Scrimba to enhance web development learning | MDN Blog Introducing the MDN HTTP Observatory | MDN Blog Static Site Generation (SSG) with Next.js | MDN Blog New JavaScript Set methods | MDN Blog
Introducing the MDN Playground: Bring your code to life! ...
Florian Dieminger June 22, 2023 6 minutes read · 2023-06-22 · via MDN Blog

At Mozilla, we are constantly striving to empower web developers and provide them with the best resources to create amazing experiences on the web. Today, we are thrilled to announce a groundbreaking addition to MDN – the MDN Playground! (Yeah, you got me, maybe this sounds a bit too exciting).

What is the MDN Playground?

The MDN Playground is pretty much what you expect it to be – a simple way to preview HTML, CSS, and JavaScript. For now it covers most of the basics; like basic autocomplete, formatting with the one and only prettier, and the option to share your amazing work (given you're logged in).

Why the MDN Playground?

This is a good question. There are plenty of playgrounds around, so why another one? Well, in theory, a playground is not that complicated to build (big <em> on theory here) and we wanted it integrated with MDN. So we thought why not write one, famous last words.

Jokes aside, the idea of having a playground on MDN had been floating around forever. Having interactive examples in our articles was a great start, but some things need more space and more freedom. Sharing your ideas and solutions is key and this should not be a third party experience. But one thing that felt like a small game changer was the integration with our existing live samples.

You can now break out any existing live sample on MDN into the Playground (look for that little Play button above the live samples). This empowers you to make some changes to the code you just read, which helps to better understand what's going on or to send a customized version to your friends. Don't get lost there yet, but check out this random animation example that you can now remix.

<h1>Hello MDN Playground</h1>
h1 {
  text-shadow:
    1px 1px 1px red,
    2px 2px 1px green,
    3px 3px 3px blue;
}

I hope this somehow covers the why. This will allow us to unify how we display and interact with code, interactive examples being next on the list.

Key features of the MDN Playground

Some product features, pretty much true but really trying to sell this (so take it with a grain of 🧂):

Instant prototyping

The MDN Playground offers a hassle-free way to quickly prototype and test your ideas. Simply write or paste your HTML, CSS, and JavaScript code into the playground, and see the results instantly rendered in the preview area. Whether you're working on a small tweak or building a complex web application, the playground provides a space tailored to your needs.

Live interaction

In addition to static code examples, the MDN Playground brings code to life by enabling interactivity. You can interact with your code in real time, making changes and observing their effects right away. This dynamic feedback loop allows for rapid iteration and a deeper understanding of how different code snippets behave.

Expanded code examples

We know how frustrating it can be to read code examples in documentation and not be able to experiment with them immediately. With the MDN Playground, this problem will become a thing of the past. Whenever you come across a code example on MDN, you'll now find a convenient Play button. Clicking it will open the example in the MDN Playground, ready for you to explore and modify.

Collaborative capabilities

The MDN Playground also supports collaboration, making it easier than ever to work with colleagues or seek help from the community. You can share your code playgrounds with others, allowing them to view and remix your code. Whether you're collaborating on a project or receiving feedback on your code, the playground fosters a collaborative environment for developers worldwide.

How to access the MDN Playground

To start using the MDN Playground, simply visit developer.mozilla.org/en-US/play and dive into the world of interactive web development. The playground is accessible on all modern web browsers, and it's completely free to use.

The MDN Playground is not just a tool, it's a community-driven initiative. We encourage you to join our vibrant community of developers, where you can share your experiences, learn from others, and contribute to the evolution of the playground. Together, we can shape the future of web development and make it more accessible to everyone.

Conclusion

The MDN Playground marks a significant milestone in our continuous effort to empower developers with the best tools and resources. With its seamless integration into MDN Web Docs, instant prototyping capabilities, live interaction, and collaborative features, the playground is set to revolutionize the way you experiment with code.

We invite you to explore the MDN Playground today and unlock a new level of creativity and productivity in your web development journey. Let's build the web of tomorrow, together!

Happy coding! (Keep reading if you want to know how this works).

Demos

Spinning Firefox

<div class="scene">
  <div class="cube">
    <div class="face front"></div>
    <div class="face back"></div>
    <div class="face right"></div>
    <div class="face left"></div>
    <div class="face top"></div>
    <div class="face bottom"></div>
  </div>
</div>
body,
html {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.scene {
  width: 200px;
  height: 200px;
  perspective: 600px;
}

.cube {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  animation: spin 5s infinite linear;
}

.face {
  position: absolute;
  width: 100%;
  height: 100%;
  background-image: url("https://upload.wikimedia.org/wikipedia/commons/a/a0/Firefox_logo%2C_2019.svg");
  background-size: cover;
  background-position: center;
}

.front {
  transform: translateZ(100px);
}

.back {
  transform: rotateY(180deg) translateZ(100px);
}

.right {
  transform: rotateY(90deg) translateZ(100px);
}

.left {
  transform: rotateY(-90deg) translateZ(100px);
}

.top {
  transform: rotateX(90deg) translateZ(100px);
}

.bottom {
  transform: rotateX(-90deg) translateZ(100px);
}

@keyframes spin {
  0% {
    transform: rotateX(0) rotateY(0);
  }
  100% {
    transform: rotateX(360deg) rotateY(360deg);
  }
}

A keyboard

<div id="synth">
  <div class="keyboard">
    <div class="key white" data-note="C"></div>
    <div class="key black" data-note="C#"></div>
    <div class="key white" data-note="D"></div>
    <div class="key black" data-note="D#"></div>
    <div class="key white" data-note="E"></div>
    <div class="key white" data-note="F"></div>
    <div class="key black" data-note="F#"></div>
    <div class="key white" data-note="G"></div>
    <div class="key black" data-note="G#"></div>
    <div class="key white" data-note="A"></div>
    <div class="key black" data-note="A#"></div>
    <div class="key white" data-note="B"></div>
  </div>
</div>
.keyboard {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex;
  height: 150px;
  width: 310px;
  background-color: #eee;
  border: 1px solid #ccc;
  border-radius: 5px;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
}

.key {
  height: 150px;
  width: 40px;
  margin: 0 2px;
  border-radius: 0 0 5px 5px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
  cursor: pointer;
}

.white {
  background-color: #fff;
}

.black {
  position: relative;
  background-color: #333;
  height: 100px;
  width: 20px;
  margin-left: -10px;
  margin-right: -10px;
  border-radius: 5px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}

[data-note="C#"],
[data-note="D#"],
[data-note="F#"],
[data-note="G#"],
[data-note="A#"] {
  z-index: 1;
}
const notes = {
  C: 261.63,
  "C#": 277.18,
  D: 293.66,
  "D#": 311.13,
  E: 329.63,
  F: 349.23,
  "F#": 369.99,
  G: 392.0,
  "G#": 415.3,
  A: 440.0,
  "A#": 466.16,
  B: 493.88,
};

const context = new AudioContext();
const masterGain = context.createGain();
masterGain.connect(context.destination);

const oscillators = {};

function playNote(note) {
  if (oscillators[note]) return;

  const frequency = getFrequency(note);
  const oscillator = context.createOscillator();
  oscillator.type = "sawtooth";
  oscillator.frequency.setValueAtTime(frequency, context.currentTime);
  oscillator.connect(masterGain);
  oscillator.start();

  oscillators[note] = oscillator;
}

function stopNote(note) {
  if (!oscillators[note]) return;

  const oscillator = oscillators[note];
  oscillator.stop();
  oscillator.disconnect();
  delete oscillators[note];
}

function getFrequency(note) {
  return notes[note];
}

const keys = document.querySelectorAll(".key");

keys.forEach((key) => {
  const note = key.dataset.note;

  key.addEventListener("mousedown", () => {
    playNote(note);
    key.classList.add("active");
  });

  key.addEventListener("mouseup", () => {
    stopNote(note);
    key.classList.remove("active");
  });

  key.addEventListener("mouseleave", () => {
    stopNote(note);
    key.classList.remove("active");
  });
});

document.addEventListener("keydown", (e) => {
  const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
  if (!key) return;

  const note = key.dataset.note;
  playNote(note);
  key.classList.add("active");
});

document.addEventListener("keyup", (e) => {
  const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
  if (!key) return;

  const note = key.dataset.note;
  stopNote(note);
  key.classList.remove("active");
});

Under the hood

Editing

We decided to go with CodeMirror for editing. As they phrase it:

CodeMirror is a code editor component for the web. It can be used in websites to implement a text input field with support for many editing features, and has a rich programming interface to allow further extension.

We considered Monaco, but decided for a more lightweight approach. So far it feels like the right choice, let's wait for feedback.

Running and communicating

A simple runner.html in an <iframe>. There's a bit more to it. The <iframe> is pointed to <random-uuid>.mdnplay.dev so we isolate all instances. The runner.html gets the code to execute via postMessage from MDN. It doesn't do too much. Given a code object called state like { html: "some HTML", css: "some CSS", js: "some JavaScript" } that's what's happening:

// Add CSS via a style tag to head
const style = document.createElement("style");
style.textContent = state.css;
document.head.appendChild(style);

// Setting HTML to the body.
// This could just be document.body.innerHTML = state.html
// But this won't execute script tags (which we want).
setHTML(document.body, state.html);

// Add JavaScript via script tag to the body
const script = document.createElement("script");
script.textContent = state.js;
document.body.appendChild(script);

dispatchEvent(new Event("load"));

Read through the runner.html if you want to get into the details.

Sharing and formatting

Sharing your playground can be done either via using the copy markdown to clipboard button, which makes it easy to include the playground code in an MDN article or add it to an GitHub issue. If you want to share more than just the plain code and enable people to preview and interact you can create a permalink to a playground (you need to be logged in). If you receive a shared playground and the content is malicious or inappropriate you can flag the playground and we'll take care of it.

Formatting is basic but good. We just let prettier do the work and have the opinions. It's up to you to press the button, so if you prefer a nice minified one liner for your code, be our guest.

Breaking out live samples

We're still looking for the best wording here, move to playground, expand to..., and so on. We'll see. It's not that important what we call it. While working on the playground, I found it to be really useful; it changed how I use MDN for my daily work.

Earlier, live samples were built during deployment. Let's not go into the details but it was ugly, and unfortunately it still is. To support the current live samples we scrape the page to find the correct code pieces and glue them together. In a brave new world this would look like:

<pre class="html live-sample___breaking">
  <h1>Breaking the internet!</h1>
</pre>
<pre id="output"></pre>
function getLanguage(node) {
  for (const part of ["html", "css", "js"]) {
    if (node.classList.contains(part)) {
      return part;
    }
  }
  return null;
}
function getCode(cls) {
  const code = {};
  document.querySelectorAll(`pre.live-sample___${cls}`).forEach((pre) => {
    let lang = getLanguage(pre);
    if (lang === null) {
      return;
    }
    code[lang] = (code[lang] || "") + pre.textContent;
  });
  return code;
}
const output = document.getElementById("output");
output.textContent = getCode("breaking").html;

So once we have the code, we just send it to the iframe as mentioned or put it into sessionStorage and send users to the playground via the Play button.

What's next

Well, to some degree that's up to you. To tell us what you want to see, you can provide feedback in our survey.