CINXE.COM
Drawing graphics - Learn web development | MDN
<!doctype html><html lang="en-US" prefix="og: https://ogp.me/ns#"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" href="https://developer.mozilla.org/favicon-48x48.bc390275e955dacb2e65.png"/><link rel="apple-touch-icon" href="https://developer.mozilla.org/apple-touch-icon.528534bba673c38049c2.png"/><meta name="theme-color" content="#ffffff"/><link rel="manifest" href="https://developer.mozilla.org/manifest.f42880861b394dd4dc9b.json"/><link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="MDN Web Docs"/><title>Drawing graphics - Learn web development | MDN</title><link rel="alternate" title="Grafiken zeichnen" href="https://developer.mozilla.org/de/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" hrefLang="de"/><link rel="alternate" title="Dessiner des graphismes" href="https://developer.mozilla.org/fr/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" hrefLang="fr"/><link rel="alternate" title="グラフィックの描画" href="https://developer.mozilla.org/ja/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" hrefLang="ja"/><link rel="alternate" title="绘图" href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" hrefLang="zh"/><link rel="alternate" title="Drawing graphics" href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" hrefLang="en"/><link rel="preload" as="font" type="font/woff2" href="/static/media/Inter.var.c2fe3cb2b7c746f7966a.woff2" crossorigin=""/><link rel="alternate" type="application/rss+xml" title="MDN Blog RSS Feed" href="https://developer.mozilla.org/en-US/blog/rss.xml" hrefLang="en"/><meta name="description" content="At this point, you should have a useful idea of the basics of graphics programming using Canvas and WebGL and what you can do with these APIs, as well as a good idea of where to go for further information. Have fun!"/><meta property="og:url" content="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics"/><meta property="og:title" content="Drawing graphics - Learn web development | MDN"/><meta property="og:type" content="website"/><meta property="og:locale" content="en_US"/><meta property="og:description" content="At this point, you should have a useful idea of the basics of graphics programming using Canvas and WebGL and what you can do with these APIs, as well as a good idea of where to go for further information. Have fun!"/><meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.d893525a4fb5fb1f67a2.png"/><meta property="og:image:type" content="image/png"/><meta property="og:image:height" content="1080"/><meta property="og:image:width" content="1920"/><meta property="og:image:alt" content="The MDN Web Docs logo, featuring a blue accent color, displayed on a solid black background."/><meta property="og:site_name" content="MDN Web Docs"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:creator" content="MozDevNet"/><link rel="canonical" href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics"/><style media="print">.article-actions-container,.document-toc-container,.language-menu,.main-menu-toggle,.on-github,.page-footer,.place,.sidebar,.top-banner,.top-navigation-main,ul.prev-next{display:none!important}.main-page-content,.main-page-content pre{padding:2px}.main-page-content pre{border-left-width:2px}</style><script src="/static/js/gtag.js" defer=""></script><script defer="" src="/static/js/main.1b60bff1.js"></script><link href="/static/css/main.959b5ea9.css" rel="stylesheet"/></head><body><script>if(document.body.addEventListener("load",(t=>{t.target.classList.contains("interactive")&&t.target.setAttribute("data-readystate","complete")}),{capture:!0}),window&&document.documentElement){const t={light:"#ffffff",dark:"#1b1b1b"};try{const e=window.localStorage.getItem("theme");e&&(document.documentElement.className=e,document.documentElement.style.backgroundColor=t[e]);const o=window.localStorage.getItem("nop");o&&(document.documentElement.dataset.nop=o)}catch(t){console.warn("Unable to read theme from localStorage",t)}}</script><div id="root"><ul id="nav-access" class="a11y-nav"><li><a id="skip-main" href="#content">Skip to main content</a></li><li><a id="skip-search" href="#top-nav-search-input">Skip to search</a></li><li><a id="skip-select-language" href="#languages-switcher-button">Skip to select language</a></li></ul><div class="page-wrapper category-javascript document-page"><div class="top-banner loading"><section class="place top container"></section></div><div class="sticky-header-container"><header class="top-navigation "><div class="container "><div class="top-navigation-wrap"><a href="/en-US/" class="logo" aria-label="MDN homepage"><svg id="mdn-docs-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 694.9 104.4" style="enable-background:new 0 0 694.9 104.4" xml:space="preserve" role="img"><title>MDN Web Docs</title><path d="M40.3 0 11.7 92.1H0L28.5 0h11.8zm10.4 0v92.1H40.3V0h10.4zM91 0 62.5 92.1H50.8L79.3 0H91zm10.4 0v92.1H91V0h10.4z" class="logo-m"></path><path d="M627.9 95.6h67v8.8h-67v-8.8z" class="logo-_"></path><path d="M367 42h-4l-10.7 30.8h-5.5l-10.8-26h-.4l-10.5 26h-5.2L308.7 42h-3.8v-5.6H323V42h-6.5l6.8 20.4h.4l10.3-26h4.7l11.2 26h.5l5.7-20.3h-6.2v-5.6H367V42zm34.9 20c-.4 3.2-2 5.9-4.7 8.2-2.8 2.3-6.5 3.4-11.3 3.4-5.4 0-9.7-1.6-13.1-4.7-3.3-3.2-5-7.7-5-13.7 0-5.7 1.6-10.3 4.7-14s7.4-5.5 12.9-5.5c5.1 0 9.1 1.6 11.9 4.7s4.3 6.9 4.3 11.3c0 1.5-.2 3-.5 4.7h-25.6c.3 7.7 4 11.6 10.9 11.6 2.9 0 5.1-.7 6.5-2 1.5-1.4 2.5-3 3-4.9l6 .9zM394 51.3c.2-2.4-.4-4.7-1.8-6.9s-3.8-3.3-7-3.3c-3.1 0-5.3 1-6.9 3-1.5 2-2.5 4.4-2.8 7.2H394zm51 2.4c0 5-1.3 9.5-4 13.7s-6.9 6.2-12.7 6.2c-6 0-10.3-2.2-12.7-6.7-.1.4-.2 1.4-.4 2.9s-.3 2.5-.4 2.9h-7.3c.3-1.7.6-3.5.8-5.3.3-1.8.4-3.7.4-5.5V22.3h-6v-5.6H416v27c1.1-2.2 2.7-4.1 4.7-5.7 2-1.6 4.8-2.4 8.4-2.4 4.6 0 8.4 1.6 11.4 4.7 3 3.2 4.5 7.6 4.5 13.4zm-7.7.6c0-4.2-1-7.4-3-9.5-2-2.2-4.4-3.3-7.4-3.3-3.4 0-6 1.2-8 3.7-1.9 2.4-2.9 5-3 7.7V57c0 3 1 5.6 3 7.7s4.5 3.1 7.6 3.1c3.6 0 6.3-1.3 8.1-3.9 1.8-2.7 2.7-5.9 2.7-9.6zm69.2 18.5h-13.2v-7.2c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2 5.7 0 9.8 2.2 12.3 6.5V22.3h-8.6v-5.6h15.8v50.6h6v5.5zM493.2 56v-4.4c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm53.1-1.4c0 5.6-1.8 10.2-5.3 13.7s-8.2 5.3-13.9 5.3-10.1-1.7-13.4-5.1c-3.3-3.4-5-7.9-5-13.5 0-5.3 1.6-9.9 4.7-13.7 3.2-3.8 7.9-5.7 14.2-5.7s11 1.9 14.1 5.7c3 3.7 4.6 8.1 4.6 13.3zm-7.7-.2c0-4-1-7.2-3-9.5s-4.8-3.5-8.2-3.5c-3.6 0-6.4 1.2-8.3 3.7s-2.9 5.6-2.9 9.5c0 3.7.9 6.8 2.8 9.4 1.9 2.6 4.6 3.9 8.3 3.9 3.6 0 6.4-1.3 8.4-3.8 1.9-2.6 2.9-5.8 2.9-9.7zm45 5.8c-.4 3.2-1.9 6.3-4.4 9.1-2.5 2.9-6.4 4.3-11.8 4.3-5.2 0-9.4-1.6-12.6-4.8-3.2-3.2-4.8-7.7-4.8-13.7 0-5.5 1.6-10.1 4.7-13.9 3.2-3.8 7.6-5.7 13.2-5.7 2.3 0 4.6.3 6.7.8 2.2.5 4.2 1.5 6.2 2.9l1.5 9.5-5.9.7-1.3-6.1c-2.1-1.2-4.5-1.8-7.2-1.8-3.5 0-6.1 1.2-7.7 3.7-1.7 2.5-2.5 5.7-2.5 9.6 0 4.1.9 7.3 2.7 9.5 1.8 2.3 4.4 3.4 7.8 3.4 5.2 0 8.2-2.9 9.2-8.8l6.2 1.3zm34.7 1.9c0 3.6-1.5 6.5-4.6 8.5s-7 3-11.7 3c-5.7 0-10.6-1.2-14.6-3.6l1.2-8.8 5.7.6-.2 4.7c1.1.5 2.3.9 3.6 1.1s2.6.3 3.9.3c2.4 0 4.5-.4 6.5-1.3 1.9-.9 2.9-2.2 2.9-4.1 0-1.8-.8-3.1-2.3-3.8s-3.5-1.3-5.8-1.7-4.6-.9-6.9-1.4c-2.3-.6-4.2-1.6-5.7-2.9-1.6-1.4-2.3-3.5-2.3-6.3 0-4.1 1.5-6.9 4.6-8.5s6.4-2.4 9.9-2.4c2.6 0 5 .3 7.2.9 2.2.6 4.3 1.4 6.1 2.4l.8 8.8-5.8.7-.8-5.7c-2.3-1-4.7-1.6-7.2-1.6-2.1 0-3.7.4-5.1 1.1-1.3.8-2 2-2 3.8 0 1.7.8 2.9 2.3 3.6 1.5.7 3.4 1.2 5.7 1.6 2.2.4 4.5.8 6.7 1.4 2.2.6 4.1 1.6 5.7 3 1.4 1.6 2.2 3.7 2.2 6.6zM197.6 73.2h-17.1v-5.5h3.8V51.9c0-3.7-.7-6.3-2.1-7.9-1.4-1.6-3.3-2.3-5.7-2.3-3.2 0-5.6 1.1-7.2 3.4s-2.4 4.6-2.5 6.9v15.6h6v5.5h-17.1v-5.5h3.8V51.9c0-3.8-.7-6.4-2.1-7.9-1.4-1.5-3.3-2.3-5.6-2.3-3.2 0-5.5 1.1-7.2 3.3-1.6 2.2-2.4 4.5-2.5 6.9v15.8h6.9v5.5h-20.2v-5.5h6V42.4h-6.1v-5.6h13.4v6.4c1.2-2.1 2.7-3.8 4.7-5.2 2-1.3 4.4-2 7.3-2s5.3.7 7.5 2.1c2.2 1.4 3.7 3.5 4.5 6.4 1.1-2.5 2.7-4.5 4.9-6.1s4.8-2.4 7.9-2.4c3.5 0 6.5 1.1 8.9 3.3s3.7 5.6 3.7 10.2v18.2h6.1v5.5zm42.5 0h-13.2V66c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2s9.8 2.2 12.3 6.5V22.7h-8.6v-5.6h15.8v50.6h6v5.5zm-13.3-16.8V52c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm61.5 16.8H269v-5.5h6V51.9c0-3.7-.7-6.3-2.2-7.9-1.4-1.6-3.4-2.3-5.7-2.3-3.1 0-5.6 1-7.4 3s-2.8 4.4-2.9 7v15.9h6v5.5h-19.3v-5.5h6V42.4h-6.2v-5.6h13.6V43c2.6-4.6 6.8-6.9 12.7-6.9 3.6 0 6.7 1.1 9.2 3.3s3.7 5.6 3.7 10.2v18.2h6v5.4h-.2z" class="logo-text"></path></svg></a><button title="Open main menu" type="button" class="button action has-icon main-menu-toggle" aria-haspopup="menu" aria-label="Open main menu" aria-expanded="false"><span class="button-wrap"><span class="icon icon-menu "></span><span class="visually-hidden">Open main menu</span></span></button></div><div class="top-navigation-main"><nav class="main-nav" aria-label="Main menu"><ul class="main-menu nojs"><li class="top-level-entry-container "><button type="button" id="references-button" class="top-level-entry menu-toggle" aria-controls="references-menu" aria-expanded="false">References</button><a href="/en-US/docs/Web" class="top-level-entry">References</a><ul id="references-menu" class="submenu references hidden inline-submenu-lg" aria-labelledby="references-button"><li class="apis-link-container mobile-only "><a href="/en-US/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li><li class="html-link-container "><a href="/en-US/docs/Web/HTML" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Structure of content on the web</p></div></a></li><li class="css-link-container "><a href="/en-US/docs/Web/CSS" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Code used to describe document style</p></div></a></li><li class="javascript-link-container "><a href="/en-US/docs/Web/JavaScript" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">General-purpose scripting language</p></div></a></li><li class="http-link-container "><a href="/en-US/docs/Web/HTTP" class="submenu-item "><div class="submenu-icon http"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP</div><p class="submenu-item-description">Protocol for transmitting web resources</p></div></a></li><li class="apis-link-container "><a href="/en-US/docs/Web/API" class="submenu-item "><div class="submenu-icon apis"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web APIs</div><p class="submenu-item-description">Interfaces for building web applications</p></div></a></li><li class="apis-link-container "><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Extensions</div><p class="submenu-item-description">Developing extensions for web browsers</p></div></a></li><li class="apis-link-container desktop-only "><a href="/en-US/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li></ul></li><li class="top-level-entry-container active"><button type="button" id="guides-button" class="top-level-entry menu-toggle" aria-controls="guides-menu" aria-expanded="false">Guides</button><a href="/en-US/docs/Learn" class="top-level-entry">Guides</a><ul id="guides-menu" class="submenu guides hidden inline-submenu-lg" aria-labelledby="guides-button"><li class="apis-link-container mobile-only "><a href="/en-US/docs/Learn" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="apis-link-container desktop-only "><a href="/en-US/docs/Learn" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="html-link-container "><a href="/en-US/docs/Learn/HTML" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Learn to structure web content with HTML</p></div></a></li><li class="css-link-container "><a href="/en-US/docs/Learn/CSS" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Learn to style content using CSS</p></div></a></li><li class="javascript-link-container "><a href="/en-US/docs/Learn/JavaScript" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">Learn to run scripts in the browser</p></div></a></li><li class=" "><a href="/en-US/docs/Web/Accessibility" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Accessibility</div><p class="submenu-item-description">Learn to make the web accessible to all</p></div></a></li></ul></li><li class="top-level-entry-container "><button type="button" id="mdn-plus-button" class="top-level-entry menu-toggle" aria-controls="mdn-plus-menu" aria-expanded="false">Plus</button><a href="/en-US/plus" class="top-level-entry">Plus</a><ul id="mdn-plus-menu" class="submenu mdn-plus hidden inline-submenu-lg" aria-labelledby="mdn-plus-button"><li class=" "><a href="/en-US/plus" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview</div><p class="submenu-item-description">A customized MDN experience</p></div></a></li><li class=" "><a href="/en-US/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li><li class=" "><a href="/en-US/plus/updates" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Updates</div><p class="submenu-item-description">All browser compatibility updates at a glance</p></div></a></li><li class=" "><a href="/en-US/plus/docs/features/overview" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Documentation</div><p class="submenu-item-description">Learn how to use MDN Plus</p></div></a></li><li class=" "><a href="/en-US/plus/docs/faq" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">FAQ</div><p class="submenu-item-description">Frequently asked questions about MDN Plus</p></div></a></li></ul></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/curriculum/">Curriculum <sup class="new">New</sup></a></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/blog/">Blog</a></li><li class="top-level-entry-container "><button type="button" id="tools-button" class="top-level-entry menu-toggle" aria-controls="tools-menu" aria-expanded="false">Tools</button><ul id="tools-menu" class="submenu tools hidden inline-submenu-lg" aria-labelledby="tools-button"><li class=" "><a href="/en-US/play" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Playground</div><p class="submenu-item-description">Write, test and share your code</p></div></a></li><li class=" "><a href="/en-US/observatory" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP Observatory</div><p class="submenu-item-description">Scan a website for free</p></div></a></li><li class=" "><a href="/en-US/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li></ul></li></ul></nav><div class="header-search"><form action="/en-US/search" class="search-form search-widget" id="top-nav-search-form" role="search"><label id="top-nav-search-label" for="top-nav-search-input" class="visually-hidden">Search MDN</label><input aria-activedescendant="" aria-autocomplete="list" aria-controls="top-nav-search-menu" aria-expanded="false" aria-labelledby="top-nav-search-label" autoComplete="off" id="top-nav-search-input" role="combobox" type="search" class="search-input-field" name="q" placeholder=" " required="" value=""/><button type="button" class="button action has-icon clear-search-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear search input</span></span></button><button type="submit" class="button action has-icon search-button"><span class="button-wrap"><span class="icon icon-search "></span><span class="visually-hidden">Search</span></span></button><div id="top-nav-search-menu" role="listbox" aria-labelledby="top-nav-search-label"></div></form></div><div class="theme-switcher-menu"><button type="button" class="button action has-icon theme-switcher-menu small" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-theme-os-default "></span>Theme</span></button></div><ul class="auth-container"><li><a href="/users/fxa/login/authenticate/?next=%2Fen-US%2Fdocs%2FLearn%2FJavaScript%2FClient-side_web_APIs%2FDrawing_graphics" class="login-link" rel="nofollow">Log in</a></li><li><a href="/users/fxa/login/authenticate/?next=%2Fen-US%2Fdocs%2FLearn%2FJavaScript%2FClient-side_web_APIs%2FDrawing_graphics" target="_self" rel="nofollow" class="button primary mdn-plus-subscribe-link"><span class="button-wrap">Sign up for free</span></a></li></ul></div></div></header><div class="article-actions-container"><div class="container"><button type="button" class="button action has-icon sidebar-button" aria-label="Expand sidebar" aria-expanded="false" aria-controls="sidebar-quicklinks"><span class="button-wrap"><span class="icon icon-sidebar "></span></span></button><nav class="breadcrumbs-container" aria-label="Breadcrumb"><ol typeof="BreadcrumbList" vocab="https://schema.org/" aria-label="breadcrumbs"><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Guides</span></a><meta property="position" content="1"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn/JavaScript" class="breadcrumb" property="item" typeof="WebPage"><span property="name">JavaScript — Dynamic client-side scripting</span></a><meta property="position" content="2"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Client-side web APIs</span></a><meta property="position" content="3"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" class="breadcrumb-current-page" property="item" typeof="WebPage"><span property="name">Drawing graphics</span></a><meta property="position" content="4"/></li></ol></nav><div class="article-actions"><button type="button" class="button action has-icon article-actions-toggle" aria-label="Article actions"><span class="button-wrap"><span class="icon icon-ellipses "></span><span class="article-actions-dialog-heading">Article Actions</span></span></button><ul class="article-actions-entries"><li class="article-actions-entry"><div class="languages-switcher-menu open-on-focus-within"><button id="languages-switcher-button" type="button" class="button action small has-icon languages-switcher-menu" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-language "></span>English (US)</span></button><div class="hidden"><ul class="submenu language-menu " aria-labelledby="language-menu-button"><li class=" "><form class="submenu-item locale-redirect-setting"><div class="group"><label class="switch"><input type="checkbox" name="locale-redirect"/><span class="slider"></span><span class="label">Remember language</span></label><a href="https://github.com/orgs/mdn/discussions/739" rel="external noopener noreferrer" target="_blank" title="Enable this setting to automatically switch to this language when it's available. (Click to learn more.)"><span class="icon icon-question-mark "></span></a></div></form></li><li class=" "><a data-locale="de" href="/de/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" class="button submenu-item"><span>Deutsch</span><span title="Diese Übersetzung ist Teil eines Experiments."><span class="icon icon-experimental "></span></span></a></li><li class=" "><a data-locale="fr" href="/fr/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" class="button submenu-item"><span>Français</span></a></li><li class=" "><a data-locale="ja" href="/ja/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" class="button submenu-item"><span>日本語</span></a></li><li class=" "><a data-locale="zh-CN" href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" class="button submenu-item"><span>中文 (简体)</span></a></li></ul></div></div></li></ul></div></div></div></div><div class="main-wrapper"><div class="sidebar-container"><aside id="sidebar-quicklinks" class="sidebar" data-macro="LearnSidebar"><button type="button" class="button action backdrop" aria-label="Collapse sidebar"><span class="button-wrap"></span></button><nav aria-label="Related Topics" class="sidebar-inner"><header class="sidebar-actions"><section class="sidebar-filter-container"><div class="sidebar-filter "><label id="sidebar-filter-label" class="sidebar-filter-label" for="sidebar-filter-input"><span class="icon icon-filter"></span><span class="visually-hidden">Filter sidebar</span></label><input id="sidebar-filter-input" autoComplete="off" class="sidebar-filter-input-field false" type="text" placeholder="Filter" value=""/><button type="button" class="button action has-icon clear-sidebar-filter-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear filter input</span></span></button></div></section></header><div class="sidebar-inner-nav"><div class="in-nav-toc"><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">In this article</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#graphics_on_the_web">Graphics on the Web</a></li><li class="document-toc-item "><a class="document-toc-link" href="#active_learning_getting_started_with_a_canvas">Active learning: Getting started with a <canvas></a></li><li class="document-toc-item "><a class="document-toc-link" href="#2d_canvas_basics">2D canvas basics</a></li><li class="document-toc-item "><a class="document-toc-link" href="#loops_and_animations">Loops and animations</a></li><li class="document-toc-item "><a class="document-toc-link" href="#webgl">WebGL</a></li><li class="document-toc-item "><a class="document-toc-link" href="#summary">Summary</a></li><li class="document-toc-item "><a class="document-toc-link" href="#see_also">See also</a></li><li class="document-toc-item "><a class="document-toc-link" href="#examples">Examples</a></li></ul></section></div></div><div class="sidebar-body"><ol><li class="section"><a href="/en-US/docs/Learn/Getting_started_with_the_web">Complete beginners start here!</a></li><li><details><summary>Getting started with the web</summary><ol><li><a href="/en-US/docs/Learn/Getting_started_with_the_web">Getting started with the web</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/Installing_basic_software">Installing basic software</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/What_will_your_website_look_like">What will your website look like?</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/Dealing_with_files">Dealing with files</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics">HTML basics</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/CSS_basics">CSS basics</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics">JavaScript basics</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/Publishing_your_website">Publishing your website</a></li><li><a href="/en-US/docs/Learn/Getting_started_with_the_web/How_the_Web_works">How the web works</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/HTML">HTML — Structuring the web</a></li><li><details><summary>Introduction to HTML</summary><ol><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML">Introduction to HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started">Getting started with HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML">What's in the head? Metadata in HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/HTML_text_fundamentals">HTML text fundamentals</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks">Creating hyperlinks</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Advanced_text_formatting">Advanced text formatting</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure">Document and website structure</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Debugging_HTML">Debugging HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Marking_up_a_letter">Marking up a letter</a></li><li><a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/Structuring_a_page_of_content">Structuring a page of content</a></li></ol></details></li><li><details><summary>Multimedia and embedding</summary><ol><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding">Multimedia and embedding</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML">Images in HTML</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content">Video and audio content</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Other_embedding_technologies">From object to iframe — other embedding technologies</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web">Adding vector graphics to the web</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">Responsive images</a></li><li><a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding/Mozilla_splash_page">Mozilla splash page</a></li></ol></details></li><li><details><summary>HTML tables</summary><ol><li><a href="/en-US/docs/Learn/HTML/Tables">HTML tables</a></li><li><a href="/en-US/docs/Learn/HTML/Tables/Basics">HTML table basics</a></li><li><a href="/en-US/docs/Learn/HTML/Tables/Advanced">HTML table advanced features and accessibility</a></li><li><a href="/en-US/docs/Learn/HTML/Tables/Structuring_planet_data">Structuring planet data</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/CSS">CSS — Styling the web</a></li><li><details><summary>CSS first steps</summary><ol><li><a href="/en-US/docs/Learn/CSS/First_steps">CSS first steps</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/What_is_CSS">What is CSS?</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/Getting_started">Getting started with CSS</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/How_CSS_is_structured">How CSS is structured</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/How_CSS_works">How CSS works</a></li><li><a href="/en-US/docs/Learn/CSS/First_steps/Styling_a_biography_page">Styling a biography page</a></li></ol></details></li><li><details><summary>CSS building blocks</summary><ol><li><a href="/en-US/docs/Learn/CSS/Building_blocks">CSS building blocks</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors">CSS selectors</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors/Type_Class_and_ID_Selectors">Type, class, and ID selectors</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors/Attribute_selectors">Attribute selectors</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements">Pseudo-classes and pseudo-elements</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Selectors/Combinators">Combinators</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance">Cascade, specificity, and inheritance</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers">Cascade layers</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/The_box_model">The box model</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Backgrounds_and_borders">Backgrounds and borders</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Handling_different_text_directions">Handling different text directions</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Overflowing_content">Overflowing content</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Values_and_units">CSS values and units</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Sizing_items_in_CSS">Sizing items in CSS</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Images_media_form_elements">Images, media, and form elements</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Styling_tables">Styling tables</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Advanced_styling_effects">Advanced styling effects</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Debugging_CSS">Debugging CSS</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Organizing">Organizing your CSS</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Fundamental_CSS_comprehension">Fundamental CSS comprehension</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/Creating_fancy_letterheaded_paper">Creating fancy letterheaded paper</a></li><li><a href="/en-US/docs/Learn/CSS/Building_blocks/A_cool_looking_box">A cool-looking box</a></li></ol></details></li><li><details><summary>Styling text</summary><ol><li><a href="/en-US/docs/Learn/CSS/Styling_text">CSS styling text</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Fundamentals">Fundamental text and font styling</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Styling_lists">Styling lists</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Styling_links">Styling links</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Web_fonts">Web fonts</a></li><li><a href="/en-US/docs/Learn/CSS/Styling_text/Typesetting_a_homepage">Typesetting a community school homepage</a></li></ol></details></li><li><details><summary>CSS layout</summary><ol><li><a href="/en-US/docs/Learn/CSS/CSS_layout">CSS layout</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Introduction">Introduction to CSS layout</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Normal_Flow">Normal Flow</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Flexbox">Flexbox</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Grids">Grids</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Floats">Floats</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Positioning">Positioning</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Multiple-column_Layout">Multiple-column layout</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design">Responsive design</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Media_queries">Beginner's guide to media queries</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods">Legacy layout methods</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Supporting_Older_Browsers">Supporting older browsers</a></li><li><a href="/en-US/docs/Learn/CSS/CSS_layout/Fundamental_Layout_Comprehension">Fundamental layout comprehension</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/JavaScript">JavaScript — Dynamic client-side scripting</a></li><li><details><summary>JavaScript first steps</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/First_steps">JavaScript first steps</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript">What is JavaScript?</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/A_first_splash">A first splash into JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong">What went wrong? Troubleshooting JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Variables">Storing the information you need — Variables</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Math">Basic math in JavaScript — numbers and operators</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings">Handling text — strings in JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods">Useful string methods</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays">Arrays</a></li><li><a href="/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator">Silly story generator</a></li></ol></details></li><li><details><summary>JavaScript building blocks</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks">JavaScript building blocks</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/conditionals">Making decisions in your code — conditionals</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code">Looping code</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions">Functions — reusable blocks of code</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Build your own function</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">Function return values</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events">Introduction to events</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Event_bubbling">Event bubbling</a></li><li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Image_gallery">Image gallery</a></li></ol></details></li><li><details><summary>Introducing JavaScript objects</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/Objects">Introducing JavaScript objects</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics">JavaScript object basics</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_prototypes">Object prototypes</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Object-oriented_programming">Object-oriented programming</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript">Classes in JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON">Working with JSON</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_building_practice">Object building practice</a></li><li><a href="/en-US/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features">Adding features to our bouncing balls demo</a></li></ol></details></li><li><details><summary>Asynchronous JavaScript</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous">Asynchronous JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">How to use promises</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Implementing_a_promise-based_API">How to implement a promise-based API</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing_workers">Introducing workers</a></li><li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Sequencing_animations">Sequencing animations</a></li></ol></details></li><li><details open=""><summary>Client-side web APIs</summary><ol><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs">Client-side web APIs</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Introduction to web APIs</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Manipulating documents</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Fetching data from the server</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">Third-party APIs</a></li><li><em><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" aria-current="page">Drawing graphics</a></em></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">Video and Audio APIs</a></li><li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">Client-side storage</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Forms">Web forms — Working with user data</a></li><li><details><summary>Web form building blocks</summary><ol><li><a href="/en-US/docs/Learn/Forms">Web form building blocks</a></li><li><a href="/en-US/docs/Learn/Forms/Your_first_form">Your first form</a></li><li><a href="/en-US/docs/Learn/Forms/How_to_structure_a_web_form">How to structure a web form</a></li><li><a href="/en-US/docs/Learn/Forms/Basic_native_form_controls">Basic native form controls</a></li><li><a href="/en-US/docs/Learn/Forms/HTML5_input_types">The HTML5 input types</a></li><li><a href="/en-US/docs/Learn/Forms/Other_form_controls">Other form controls</a></li><li><a href="/en-US/docs/Learn/Forms/Styling_web_forms">Styling web forms</a></li><li><a href="/en-US/docs/Learn/Forms/Advanced_form_styling">Advanced form styling</a></li><li><a href="/en-US/docs/Learn/Forms/UI_pseudo-classes">UI pseudo-classes</a></li><li><a href="/en-US/docs/Learn/Forms/Form_validation">Client-side form validation</a></li><li><a href="/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data">Sending form data</a></li></ol></details></li><li><details><summary>Advanced web form techniques</summary><ol><li><a href="/en-US/docs/Learn/Forms/How_to_build_custom_form_controls">How to build custom form controls</a></li><li><a href="/en-US/docs/Learn/Forms/Sending_forms_through_JavaScript">Sending forms through JavaScript</a></li><li><a href="/en-US/docs/Learn/Forms/Property_compatibility_table_for_form_controls">CSS property compatibility table for form controls</a></li><li><a href="/en-US/docs/Learn/Forms/HTML_forms_in_legacy_browsers">HTML forms in legacy browsers</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Accessibility">Accessibility — Make the web usable by everyone</a></li><li><details><summary>Accessibility guides</summary><ol><li><a href="/en-US/docs/Learn/Accessibility">Accessibility</a></li><li><a href="/en-US/docs/Learn/Accessibility/What_is_accessibility">What is accessibility?</a></li><li><a href="/en-US/docs/Learn/Accessibility/HTML">HTML: A good basis for accessibility</a></li><li><a href="/en-US/docs/Learn/Accessibility/CSS_and_JavaScript">CSS and JavaScript accessibility best practices</a></li><li><a href="/en-US/docs/Learn/Accessibility/WAI-ARIA_basics">WAI-ARIA basics</a></li><li><a href="/en-US/docs/Learn/Accessibility/Multimedia">Accessible multimedia</a></li><li><a href="/en-US/docs/Learn/Accessibility/Mobile">Mobile accessibility</a></li><li><a href="/en-US/docs/Learn/Accessibility/Accessibility_troubleshooting">Assessment: Accessibility troubleshooting</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Performance">Performance — Making websites fast and responsive</a></li><li><details><summary>Performance guides</summary><ol><li><a href="/en-US/docs/Learn/Performance">Web performance</a></li><li><a href="/en-US/docs/Learn/Performance/why_web_performance">The "why" of web performance</a></li><li><a href="/en-US/docs/Learn/Performance/What_is_web_performance">What is web performance?</a></li><li><a href="/en-US/docs/Learn/Performance/Perceived_performance">Perceived performance</a></li><li><a href="/en-US/docs/Learn/Performance/Measuring_performance">Measuring performance</a></li><li><a href="/en-US/docs/Learn/Performance/Multimedia">Multimedia: Images</a></li><li><a href="/en-US/docs/Learn/Performance/video">Multimedia: video</a></li><li><a href="/en-US/docs/Learn/Performance/JavaScript">JavaScript performance optimization</a></li><li><a href="/en-US/docs/Learn/Performance/HTML">HTML performance optimization</a></li><li><a href="/en-US/docs/Learn/Performance/CSS">CSS performance optimization</a></li><li><a href="/en-US/docs/Learn/Performance/business_case_for_performance">The business case for web performance</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/MathML">MathML — Writing mathematics with MathML</a></li><li><details><summary>MathML first steps</summary><ol><li><a href="/en-US/docs/Learn/MathML/First_steps">MathML first steps</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Getting_started">Getting started with MathML</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Text_containers">MathML Text Containers</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Fractions_and_roots">MathML fractions and roots</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Scripts">MathML scripted elements</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Tables">MathML tables</a></li><li><a href="/en-US/docs/Learn/MathML/First_steps/Three_famous_mathematical_formulas">Three famous mathematical formulas</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/../Games">Games — Developing games for the web</a></li><li><details><summary>Guides and tutorials</summary><ol><li><a href="/en-US/docs/Games/Introduction">Introduction to game development for the Web</a></li><li><a href="/en-US/docs/Games/Techniques">Techniques for game development</a></li><li><a href="/en-US/docs/Games/Tutorials">Tutorials</a></li><li><a href="/en-US/docs/Games/Publishing_games">Publishing games</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Tools_and_testing">Tools and testing</a></li><li><details><summary>Client-side web development tools</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools">Understanding client-side web development tools</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview">Client-side tooling overview</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line">Command line crash course</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management">Package management basics</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Introducing_complete_toolchain">Introducing a complete toolchain</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Deployment">Deploying our app</a></li></ol></details></li><li><details><summary>Introduction to client-side frameworks</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction">Introduction to client-side frameworks</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features">Framework main features</a></li></ol></details></li><li><details><summary>React</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started">Getting started with React</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning">Beginning our React todo list</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components">Componentizing our React app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state">React interactivity: Events and state</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering">React interactivity: Editing, filtering, conditional rendering</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility">Accessibility in React</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources">React resources</a></li></ol></details></li><li><details><summary>Ember</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started">Getting started with Ember</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization">Ember app structure and componentization</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state">Ember interactivity: Events, classes and state</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer">Ember Interactivity: Footer functionality, conditional rendering</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing">Routing in Ember</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources">Ember resources and troubleshooting</a></li></ol></details></li><li><details><summary>Vue</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started">Getting started with Vue</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component">Creating our first Vue component</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_rendering_lists">Rendering a list of Vue components</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_methods_events_models">Adding a new todo form: Vue events, methods, and models</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_styling">Styling Vue components with CSS</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties">Using Vue computed properties</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_conditional_rendering">Vue conditional rendering: editing existing todos</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management">Vue refs and lifecycle methods for focus management</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_resources">Vue resources</a></li></ol></details></li><li><details><summary>Svelte</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started">Getting started with Svelte</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning">Starting our Svelte to-do list app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props">Dynamic behavior in Svelte: working with variables and props</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components">Componentizing our Svelte app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility">Advanced Svelte: Reactivity, lifecycle, accessibility</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores">Working with Svelte stores</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript">TypeScript support in Svelte</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_deployment_next">Deployment and next steps</a></li></ol></details></li><li><details><summary>Angular</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_getting_started">Getting started with Angular</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning">Beginning our Angular todo list app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_styling">Styling our Angular app</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_item_component">Creating an item component</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_filtering">Filtering our to-do items</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_building">Building Angular applications and further resources</a></li></ol></details></li><li><details><summary>Git and GitHub</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/GitHub">Git and GitHub</a></li></ol></details></li><li><details><summary>Cross browser testing</summary><ol><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing">Cross browser testing</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Introduction">Introduction to cross-browser testing</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies">Strategies for carrying out testing</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS">Handling common HTML and CSS problems</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript">Handling common JavaScript problems</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility">Handling common accessibility problems</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection">Implementing feature detection</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Automated_testing">Introduction to automated testing</a></li><li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Setting up your own test automation environment</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Server-side">Server-side website programming</a></li><li><details><summary>First steps</summary><ol><li><a href="/en-US/docs/Learn/Server-side/First_steps">Server-side website programming first steps</a></li><li><a href="/en-US/docs/Learn/Server-side/First_steps/Introduction">Introduction to the server side</a></li><li><a href="/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview">Client-Server Overview</a></li><li><a href="/en-US/docs/Learn/Server-side/First_steps/Web_frameworks">Server-side web frameworks</a></li><li><a href="/en-US/docs/Learn/Server-side/First_steps/Website_security">Website security</a></li></ol></details></li><li><details><summary>Django web framework (Python)</summary><ol><li><a href="/en-US/docs/Learn/Server-side/Django">Django Web Framework (Python)</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django introduction</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">Setting up a Django development environment</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django Tutorial Part 3: Using models</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Part 4: Django admin site</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django Tutorial Part 8: User authentication and permissions</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django Tutorial Part 11: Deploying Django to production</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django web application security</a></li><li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">Assessment: DIY Django mini blog</a></li></ol></details></li><li><details><summary>Express Web Framework (Node.js/JavaScript)</summary><ol><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs">Express web framework (Node.js/JavaScript)</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node development environment</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li><li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn/Common_questions">Further resources</a></li><li><details><summary>Common questions</summary><ol><li><a href="/en-US/docs/Learn/Common_questions">Common questions</a></li><li><a href="/en-US/docs/Learn/HTML/Howto">Use HTML to solve common problems</a></li><li><a href="/en-US/docs/Learn/CSS/Howto">Use CSS to solve common problems</a></li><li><a href="/en-US/docs/Learn/JavaScript/Howto">Solve common problems in your JavaScript code</a></li><li><a href="/en-US/docs/Learn/Common_questions/Web_mechanics">Web mechanics</a></li><li><a href="/en-US/docs/Learn/Common_questions/Tools_and_setup">Tools and setup</a></li><li><a href="/en-US/docs/Learn/Common_questions/Design_and_accessibility">Design and accessibility</a></li></ol></details></li></ol></div></div><section class="place side"></section></nav></aside><div class="toc-container"><aside class="toc"><nav><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">In this article</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#graphics_on_the_web">Graphics on the Web</a></li><li class="document-toc-item "><a class="document-toc-link" href="#active_learning_getting_started_with_a_canvas">Active learning: Getting started with a <canvas></a></li><li class="document-toc-item "><a class="document-toc-link" href="#2d_canvas_basics">2D canvas basics</a></li><li class="document-toc-item "><a class="document-toc-link" href="#loops_and_animations">Loops and animations</a></li><li class="document-toc-item "><a class="document-toc-link" href="#webgl">WebGL</a></li><li class="document-toc-item "><a class="document-toc-link" href="#summary">Summary</a></li><li class="document-toc-item "><a class="document-toc-link" href="#see_also">See also</a></li><li class="document-toc-item "><a class="document-toc-link" href="#examples">Examples</a></li></ul></section></div></nav></aside><section class="place side"></section></div></div><main id="content" class="main-content "><article class="main-page-content" lang="en-US"><header><h1>Drawing graphics</h1></header><div class="section-content"><ul class="prev-next"> <li><a class="button secondary" href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs"><span class="button-wrap"> Previous </span></a></li> <li><a class="button secondary" href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs"><span class="button-wrap"> Overview: Client-side web APIs</span></a></li> <li><a class="button secondary" href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs"><span class="button-wrap"> Next </span></a></li> </ul> <p>The browser contains some very powerful graphics programming tools, from the Scalable Vector Graphics (<a href="/en-US/docs/Web/SVG">SVG</a>) language, to APIs for drawing on HTML <a href="/en-US/docs/Web/HTML/Element/canvas"><code><canvas></code></a> elements, (see <a href="/en-US/docs/Web/API/Canvas_API">The Canvas API</a> and <a href="/en-US/docs/Web/API/WebGL_API">WebGL</a>). This article provides an introduction to canvas, and further resources to allow you to learn more.</p> <figure class="table-container"><table> <tbody> <tr> <th scope="row">Prerequisites:</th> <td> JavaScript basics (see <a href="/en-US/docs/Learn/JavaScript/First_steps">first steps</a>, <a href="/en-US/docs/Learn/JavaScript/Building_blocks">building blocks</a>, <a href="/en-US/docs/Learn/JavaScript/Objects">JavaScript objects</a>), the <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">basics of Client-side APIs</a> </td> </tr> <tr> <th scope="row">Objective:</th> <td> To learn the basics of drawing on <code><canvas></code> elements using JavaScript. </td> </tr> </tbody> </table></figure></div><section aria-labelledby="graphics_on_the_web"><h2 id="graphics_on_the_web"><a href="#graphics_on_the_web">Graphics on the Web</a></h2><div class="section-content"><p>As we talked about in our HTML <a href="/en-US/docs/Learn/HTML/Multimedia_and_embedding">Multimedia and embedding</a> module, the Web was originally just text, which was very boring, so images were introduced — first via the <a href="/en-US/docs/Web/HTML/Element/img"><code><img></code></a> element and later via CSS properties such as <a href="/en-US/docs/Web/CSS/background-image"><code>background-image</code></a>, and <a href="/en-US/docs/Web/SVG">SVG</a>.</p> <p>This however was still not enough. While you could use <a href="/en-US/docs/Learn/CSS">CSS</a> and <a href="/en-US/docs/Learn/JavaScript">JavaScript</a> to animate (and otherwise manipulate) SVG vector images — as they are represented by markup — there was still no way to do the same for bitmap images, and the tools available were rather limited. The Web still had no way to effectively create animations, games, 3D scenes, and other requirements commonly handled by lower level languages such as C++ or Java.</p> <p>The situation started to improve when browsers began to support the <a href="/en-US/docs/Web/HTML/Element/canvas"><code><canvas></code></a> element and associated <a href="/en-US/docs/Web/API/Canvas_API">Canvas API</a> in 2004. As you'll see below, canvas provides some useful tools for creating 2D animations, games, data visualizations, and other types of applications, especially when combined with some of the other APIs the web platform provides, but can be difficult or impossible to make accessible</p> <p>The below example shows a simple 2D canvas-based bouncing balls animation that we originally met in our <a href="/en-US/docs/Learn/JavaScript/Objects/Object_building_practice">Introducing JavaScript objects</a> module:</p><iframe width="100%" height="500" src="https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html" loading="lazy"></iframe> <p>Around 2006–2007, Mozilla started work on an experimental 3D canvas implementation. This became <a href="/en-US/docs/Web/API/WebGL_API">WebGL</a>, which gained traction among browser vendors, and was standardized around 2009–2010. WebGL allows you to create real 3D graphics inside your web browser; the below example shows a simple rotating WebGL cube:</p><iframe width="100%" height="500" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html" loading="lazy"></iframe> <p>This article will focus mainly on 2D canvas, as raw WebGL code is very complex. We will however show how to use a WebGL library to create a 3D scene more easily, and you can find a tutorial covering raw WebGL elsewhere — see <a href="/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL">Getting started with WebGL</a>.</p></div></section><section aria-labelledby="active_learning_getting_started_with_a_canvas"><h2 id="active_learning_getting_started_with_a_canvas"><a href="#active_learning_getting_started_with_a_canvas">Active learning: Getting started with a <canvas></a></h2><div class="section-content"><p>If you want to create a 2D <em>or</em> 3D scene on a web page, you need to start with an HTML <a href="/en-US/docs/Web/HTML/Element/canvas"><code><canvas></code></a> element. This element is used to define the area on the page into which the image will be drawn. This is as simple as including the element on the page:</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code><canvas width="320" height="240"></canvas> </code></pre></div> <p>This will create a canvas on the page with a size of 320 by 240 pixels.</p> <p>You should put some fallback content inside the <code><canvas></code> tags. This should describe the canvas content to users of browsers that don't support canvas, or users of screen readers.</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code><canvas width="320" height="240"> <p>Description of the canvas for those unable to view it.</p> </canvas> </code></pre></div> <p>The fallback should provide useful alternative content to the canvas content. For example, if you are rendering a constantly updating graph of stock prices, the fallback content could be a static image of the latest stock graph, with <code>alt</code> text saying what the prices are in text or a list of links to individual stock pages.</p> <div class="notecard note"> <p><strong>Note:</strong> Canvas content is not accessible to screen readers. Include descriptive text as the value of the <a href="/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label"><code>aria-label</code></a> attribute directly on the canvas element itself or include fallback content placed within the opening and closing <code><canvas></code> tags. Canvas content is not part of the DOM, but nested fallback content is.</p> </div></div></section><section aria-labelledby="creating_and_sizing_our_canvas"><h3 id="creating_and_sizing_our_canvas"><a href="#creating_and_sizing_our_canvas">Creating and sizing our canvas</a></h3><div class="section-content"><p>Let's start by creating our own canvas that we draw future experiments on to.</p> <ol> <li> <p>First make a local copy of the <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/0_canvas_start" class="external" target="_blank">0_canvas_start</a> directory. It contains three files:</p> <ul> <li>"index.html"</li> <li>"script.js"</li> <li>"style.css"</li> </ul> </li> <li> <p>Open "index.html", and add the following code into it, just below the opening <a href="/en-US/docs/Web/HTML/Element/body"><code><body></code></a> tag:</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code><canvas class="myCanvas"> <p>Add suitable fallback here.</p> </canvas> </code></pre></div> <p>We have added a <code>class</code> to the <code><canvas></code> element so it will be easier to select if we have multiple canvases on the page, but we have removed the <code>width</code> and <code>height</code> attributes for now (you could add them back in if you wanted, but we will set them using JavaScript in a below section). Canvases with no explicit width and height default to 300 pixels wide by 150 pixels high.</p> </li> <li> <p>Now open "script.js" and add the following lines of JavaScript:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const canvas = document.querySelector(".myCanvas"); const width = (canvas.width = window.innerWidth); const height = (canvas.height = window.innerHeight); </code></pre></div> <p>Here we have stored a reference to the canvas in the <code>canvas</code> constant. In the second line we set both a new constant <code>width</code> and the canvas' <code>width</code> property equal to <a href="/en-US/docs/Web/API/Window/innerWidth"><code>Window.innerWidth</code></a> (which gives us the viewport width). In the third line we set both a new constant <code>height</code> and the canvas' <code>height</code> property equal to <a href="/en-US/docs/Web/API/Window/innerHeight"><code>Window.innerHeight</code></a> (which gives us the viewport height). So now we have a canvas that fills the entire width and height of the browser window!</p> <p>You'll also see that we are chaining assignments together with multiple equals signs — this is allowed in JavaScript, and it is a good technique if you want to make multiple variables all equal to the same value. We wanted to make the canvas width and height easily accessible in the width/height variables, as they are useful values to have available for later (for example, if you want to draw something exactly halfway across the width of the canvas).</p> </li> </ol> <div class="notecard note"> <p><strong>Note:</strong> You should generally set the size of the image using HTML attributes or DOM properties, as explained above. You could use CSS, but the trouble then is that the sizing is done after the canvas has rendered, and just like any other image (the rendered canvas is just an image), the image could become pixelated/distorted.</p> </div></div></section><section aria-labelledby="getting_the_canvas_context_and_final_setup"><h3 id="getting_the_canvas_context_and_final_setup"><a href="#getting_the_canvas_context_and_final_setup">Getting the canvas context and final setup</a></h3><div class="section-content"><p>We need to do one final thing before we can consider our canvas template finished. To draw onto the canvas we need to get a special reference to the drawing area called a context. This is done using the <a href="/en-US/docs/Web/API/HTMLCanvasElement/getContext"><code>HTMLCanvasElement.getContext()</code></a> method, which for basic usage takes a single string as a parameter representing the type of context you want to retrieve.</p> <p>In this case we want a 2d canvas, so add the following JavaScript line below the others in "script.js":</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const ctx = canvas.getContext("2d"); </code></pre></div> <div class="notecard note"> <p><strong>Note:</strong> Other context values you could choose include <code>webgl</code> for WebGL, <code>webgl2</code> for WebGL 2, etc., but we won't need those in this article.</p> </div> <p>So that's it — our canvas is now primed and ready for drawing on! The <code>ctx</code> variable now contains a <a href="/en-US/docs/Web/API/CanvasRenderingContext2D"><code>CanvasRenderingContext2D</code></a> object, and all drawing operations on the canvas will involve manipulating this object.</p> <p>Let's do one last thing before we move on. We'll color the canvas background black to give you a first taste of the canvas API. Add the following lines at the bottom of your JavaScript:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(0 0 0)"; ctx.fillRect(0, 0, width, height); </code></pre></div> <p>Here we are setting a fill color using the canvas' <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle" title="fillStyle"><code>fillStyle</code></a> property (this takes <a href="/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#color">color values</a> just like CSS properties do), then drawing a rectangle that covers the entire area of the canvas with the <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fillRect" title="fillRect"><code>fillRect</code></a> method (the first two parameters are the coordinates of the rectangle's top left-hand corner; the last two are the width and height you want the rectangle drawn at — we told you those <code>width</code> and <code>height</code> variables would be useful)!</p> <p>OK, our template is done and it's time to move on.</p></div></section><section aria-labelledby="2d_canvas_basics"><h2 id="2d_canvas_basics"><a href="#2d_canvas_basics">2D canvas basics</a></h2><div class="section-content"><p>As we said above, all drawing operations are done by manipulating a <a href="/en-US/docs/Web/API/CanvasRenderingContext2D"><code>CanvasRenderingContext2D</code></a> object (in our case, <code>ctx</code>). Many operations need to be given coordinates to pinpoint exactly where to draw something — the top left of the canvas is point (0, 0), the horizontal (x) axis runs from left to right, and the vertical (y) axis runs from top to bottom.</p> <p> <img src="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics/canvas_default_grid.png" alt="Gridded graph paper with small squares covering its area with a steelblue square in the middle. The top left corner of the canvas is point (0, 0) of the canvas x-axis and y-axis. The horizontal (x) axis runs from left to right denoting the width, and the vertical (y) axis runs from top to bottom denotes the height. The top left corner of the blue square is labeled as being a distance of x units from the y-axis and y units from the x-axis." width="220" height="220" loading="lazy"> </p> <p>Drawing shapes tends to be done using the rectangle shape primitive, or by tracing a line along a certain path and then filling in the shape. Below we'll show how to do both.</p></div></section><section aria-labelledby="simple_rectangles"><h3 id="simple_rectangles"><a href="#simple_rectangles">Simple rectangles</a></h3><div class="section-content"><p>Let's start with some simple rectangles.</p> <ol> <li> <p>First of all, take a copy of your newly coded canvas template (or make a local copy of the <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a> directory if you didn't follow the above steps).</p> </li> <li> <p>Next, add the following lines to the bottom of your JavaScript:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(255 0 0)"; ctx.fillRect(50, 50, 100, 150); </code></pre></div> <p>If you save and refresh, you should see a red rectangle has appeared on your canvas. Its top left corner is 50 pixels away from the top and left of the canvas edge (as defined by the first two parameters), and it is 100 pixels wide and 150 pixels tall (as defined by the third and fourth parameters).</p> </li> <li> <p>Let's add another rectangle into the mix — a green one this time. Add the following at the bottom of your JavaScript:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(0 255 0)"; ctx.fillRect(75, 75, 100, 100); </code></pre></div> <p>Save and refresh, and you'll see your new rectangle. This raises an important point: graphics operations like drawing rectangles, lines, and so forth are performed in the order in which they occur. Think of it like painting a wall, where each coat of paint overlaps and may even hide what's underneath. You can't do anything to change this, so you have to think carefully about the order in which you draw the graphics.</p> </li> <li> <p>Note that you can draw semi-transparent graphics by specifying a semi-transparent color, for example by using <code>rgb()</code>. The "alpha channel" defines the amount of transparency the color has. The higher its value, the more it will obscure whatever's behind it. Add the following to your code:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(255 0 255 / 75%)"; ctx.fillRect(25, 100, 175, 50); </code></pre></div> </li> <li> <p>Now try drawing some more rectangles of your own; have fun!</p> </li> </ol></div></section><section aria-labelledby="strokes_and_line_widths"><h3 id="strokes_and_line_widths"><a href="#strokes_and_line_widths">Strokes and line widths</a></h3><div class="section-content"><p>So far we've looked at drawing filled rectangles, but you can also draw rectangles that are just outlines (called <strong>strokes</strong> in graphic design). To set the color you want for your stroke, you use the <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/strokeStyle" title="strokeStyle"><code>strokeStyle</code></a> property; drawing a stroke rectangle is done using <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/strokeRect" title="strokeRect"><code>strokeRect</code></a>.</p> <ol> <li> <p>Add the following to the previous example, again below the previous JavaScript lines:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.strokeStyle = "rgb(255 255 255)"; ctx.strokeRect(25, 25, 175, 200); </code></pre></div> </li> <li> <p>The default width of strokes is 1 pixel; you can adjust the <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/lineWidth" title="lineWidth"><code>lineWidth</code></a> property value to change this (it takes a number representing the number of pixels wide the stroke is). Add the following line in between the previous two lines:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.lineWidth = 5; </code></pre></div> </li> </ol> <p>Now you should see that your white outline has become much thicker! That's it for now. At this point your example should look like this:</p><iframe width="100%" height="250" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>Note:</strong> The finished code is available on GitHub as <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles" class="external" target="_blank">2_canvas_rectangles</a>.</p> </div></div></section><section aria-labelledby="drawing_paths"><h3 id="drawing_paths"><a href="#drawing_paths">Drawing paths</a></h3><div class="section-content"><p>If you want to draw anything more complex than a rectangle, you need to draw a path. Basically, this involves writing code to specify exactly what path the pen should move along on your canvas to trace the shape you want to draw. Canvas includes functions for drawing straight lines, circles, Bézier curves, and more.</p> <p>Let's start the section off by making a fresh copy of our canvas template (<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>), in which to draw the new example.</p> <p>We'll be using some common methods and properties across all of the below sections:</p> <ul> <li><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath" title="beginPath()"><code>beginPath()</code></a> — start drawing a path at the point where the pen currently is on the canvas. On a new canvas, the pen starts out at (0, 0).</li> <li><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/moveTo" title="moveTo()"><code>moveTo()</code></a> — move the pen to a different point on the canvas, without recording or tracing the line; the pen "jumps" to the new position.</li> <li><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fill" title="fill()"><code>fill()</code></a> — draw a filled shape by filling in the path you've traced so far.</li> <li><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/stroke" title="stroke()"><code>stroke()</code></a> — draw an outline shape by drawing a stroke along the path you've drawn so far.</li> <li>You can also use features like <code>lineWidth</code> and <code>fillStyle</code>/<code>strokeStyle</code> with paths as well as rectangles.</li> </ul> <p>A typical, simple path-drawing operation would look something like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(255 0 0)"; ctx.beginPath(); ctx.moveTo(50, 50); // draw your path ctx.fill(); </code></pre></div> <h4 id="drawing_lines">Drawing lines</h4> <p>Let's draw an equilateral triangle on the canvas.</p> <ol> <li> <p>First of all, add the following helper function to the bottom of your code. This converts degree values to radians, which is useful because whenever you need to provide an angle value in JavaScript, it will nearly always be in radians, but humans usually think in degrees.</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function degToRad(degrees) { return (degrees * Math.PI) / 180; } </code></pre></div> </li> <li> <p>Next, start off your path by adding the following below your previous addition; here we set a color for our triangle, start drawing a path, and then move the pen to (50, 50) without drawing anything. That's where we'll start drawing our triangle.</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(255 0 0)"; ctx.beginPath(); ctx.moveTo(50, 50); </code></pre></div> </li> <li> <p>Now add the following lines at the bottom of your script:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.lineTo(150, 50); const triHeight = 50 * Math.tan(degToRad(60)); ctx.lineTo(100, 50 + triHeight); ctx.lineTo(50, 50); ctx.fill(); </code></pre></div> <p>Let's run through this in order:</p> <p>First we draw a line across to (150, 50) — our path now goes 100 pixels to the right along the x axis.</p> <p>Second, we work out the height of our equilateral triangle, using a bit of simple trigonometry. Basically, we are drawing the triangle pointing downwards. The angles in an equilateral triangle are always 60 degrees; to work out the height we can split it down the middle into two right-angled triangles, which will each have angles of 90 degrees, 60 degrees, and 30 degrees. In terms of the sides:</p> <ul> <li>The longest side is called the <strong>hypotenuse</strong></li> <li>The side next to the 60 degree angle is called the <strong>adjacent</strong> — which we know is 50 pixels, as it is half of the line we just drew.</li> <li>The side opposite the 60 degree angle is called the <strong>opposite</strong>, which is the height of the triangle we want to calculate.</li> </ul> <p> <img src="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics/trigonometry.png" alt="An equilateral triangle pointing downwards with labeled angles and sides. The horizontal line at the top is labeled 'adjacent'. A perpendicular dotted line, from the middle of the adjacent line, labeled 'opposite', splits the triangle creating two equal right triangles. The right side of the triangle is labeled the hypotenuse, as it is the hypotenuse of the right triangle formed by the line labeled 'opposite'. while all three-sided of the triangle are of equal length, the hypotenuse is the longest side of the right triangle." width="200" height="166" loading="lazy"> </p> <p>One of the basic trigonometric formulae states that the length of the adjacent multiplied by the tangent of the angle is equal to the opposite, hence we come up with <code>50 * Math.tan(degToRad(60))</code>. We use our <code>degToRad()</code> function to convert 60 degrees to radians, as <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan"><code>Math.tan()</code></a> expects an input value in radians.</p> </li> <li> <p>With the height calculated, we draw another line to <code>(100, 50 + triHeight)</code>. The X coordinate is simple; it must be halfway between the previous two X values we set. The Y value on the other hand must be 50 plus the triangle height, as we know the top of the triangle is 50 pixels from the top of the canvas.</p> </li> <li> <p>The next line draws a line back to the starting point of the triangle.</p> </li> <li> <p>Last of all, we run <code>ctx.fill()</code> to end the path and fill in the shape.</p> </li> </ol> <h4 id="drawing_circles">Drawing circles</h4> <p>Now let's look at how to draw a circle in canvas. This is accomplished using the <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/arc" title="arc()"><code>arc()</code></a> method, which draws all or part of a circle at a specified point.</p> <ol> <li> <p>Let's add an arc to our canvas — add the following to the bottom of your code:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(0 0 255)"; ctx.beginPath(); ctx.arc(150, 106, 50, degToRad(0), degToRad(360), false); ctx.fill(); </code></pre></div> <p><code>arc()</code> takes six parameters. The first two specify the position of the arc's center (X and Y, respectively). The third is the circle's radius, the fourth and fifth are the start and end angles at which to draw the circle (so specifying 0 and 360 degrees gives us a full circle), and the sixth parameter defines whether the circle should be drawn counterclockwise (anticlockwise) or clockwise (<code>false</code> is clockwise).</p> <div class="notecard note"> <p><strong>Note:</strong> 0 degrees is horizontally to the right.</p> </div> </li> <li> <p>Let's try adding another arc:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "yellow"; ctx.beginPath(); ctx.arc(200, 106, 50, degToRad(-45), degToRad(45), true); ctx.lineTo(200, 106); ctx.fill(); </code></pre></div> <p>The pattern here is very similar, but with two differences:</p> <ul> <li>We have set the last parameter of <code>arc()</code> to <code>true</code>, meaning that the arc is drawn counterclockwise, which means that even though the arc is specified as starting at -45 degrees and ending at 45 degrees, we draw the arc around the 270 degrees not inside this portion. If you were to change <code>true</code> to <code>false</code> and then re-run the code, only the 90 degree slice of the circle would be drawn.</li> <li>Before calling <code>fill()</code>, we draw a line to the center of the circle. This means that we get the rather nice Pac-Man-style cutout rendered. If you removed this line (try it!) then re-ran the code, you'd get just an edge of the circle chopped off between the start and end point of the arc. This illustrates another important point of the canvas — if you try to fill an incomplete path (i.e. one that is not closed), the browser fills in a straight line between the start and end point and then fills it in.</li> </ul> </li> </ol> <p>That's it for now; your final example should look like this:</p><iframe width="100%" height="200" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/3_canvas_paths/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>Note:</strong> The finished code is available on GitHub as <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/3_canvas_paths" class="external" target="_blank">3_canvas_paths</a>.</p> </div> <div class="notecard note"> <p><strong>Note:</strong> To find out more about advanced path drawing features such as Bézier curves, check out our <a href="/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes">Drawing shapes with canvas</a> tutorial.</p> </div></div></section><section aria-labelledby="text"><h3 id="text"><a href="#text">Text</a></h3><div class="section-content"><p>Canvas also has features for drawing text. Let's explore these briefly. Start by making another fresh copy of our canvas template (<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>) in which to draw the new example.</p> <p>Text is drawn using two methods:</p> <ul> <li><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fillText" title="fillText()"><code>fillText()</code></a> — draws filled text.</li> <li><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/strokeText" title="strokeText()"><code>strokeText()</code></a> — draws outline (stroke) text.</li> </ul> <p>Both of these take three properties in their basic usage: the text string to draw and the X and Y coordinates of the point to start drawing the text at. This works out as the <strong>bottom left</strong> corner of the <strong>text box</strong> (literally, the box surrounding the text you draw), which might confuse you as other drawing operations tend to start from the top left corner — bear this in mind.</p> <p>There are also a number of properties to help control text rendering such as <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/font" title="font"><code>font</code></a>, which lets you specify font family, size, etc. It takes as its value the same syntax as the CSS <a href="/en-US/docs/Web/CSS/font"><code>font</code></a> property.</p> <p>Canvas content is not accessible to screen readers. Text painted to the canvas is not available to the DOM, but must be made available to be accessible. In this example, we include the text as the value for <code>aria-label</code>.</p> <p>Try adding the following block to the bottom of your JavaScript:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.strokeStyle = "white"; ctx.lineWidth = 1; ctx.font = "36px arial"; ctx.strokeText("Canvas text", 50, 50); ctx.fillStyle = "red"; ctx.font = "48px georgia"; ctx.fillText("Canvas text", 50, 150); canvas.setAttribute("aria-label", "Canvas text"); </code></pre></div> <p>Here we draw two lines of text, one outline and the other stroke. The final example should look like so:</p><iframe width="100%" height="180" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/4_canvas_text/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>Note:</strong> The finished code is available on GitHub as <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/4_canvas_text" class="external" target="_blank">4_canvas_text</a>.</p> </div> <p>Have a play and see what you can come up with! You can find more information on the options available for canvas text at <a href="/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text">Drawing text</a>.</p></div></section><section aria-labelledby="drawing_images_onto_canvas"><h3 id="drawing_images_onto_canvas"><a href="#drawing_images_onto_canvas">Drawing images onto canvas</a></h3><div class="section-content"><p>It is possible to render external images onto your canvas. These can be simple images, frames from videos, or the content of other canvases. For the moment we'll just look at the case of using some simple images on our canvas.</p> <ol> <li> <p>As before, make another fresh copy of our canvas template (<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>) in which to draw the new example.</p> <p>Images are drawn onto canvas using the <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage" title="drawImage()"><code>drawImage()</code></a> method. The simplest version takes three parameters — a reference to the image you want to render, and the X and Y coordinates of the image's top left corner.</p> </li> <li> <p>Let's start by getting an image source to embed in our canvas. Add the following lines to the bottom of your JavaScript:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const image = new Image(); image.src = "firefox.png"; </code></pre></div> <p>Here we create a new <a href="/en-US/docs/Web/API/HTMLImageElement"><code>HTMLImageElement</code></a> object using the <a href="/en-US/docs/Web/API/HTMLImageElement/Image" title="Image()"><code>Image()</code></a> constructor. The returned object is the same type as that which is returned when you grab a reference to an existing <a href="/en-US/docs/Web/HTML/Element/img"><code><img></code></a> element. We then set its <a href="/en-US/docs/Web/HTML/Element/img#src"><code>src</code></a> attribute to equal our Firefox logo image. At this point, the browser starts loading the image.</p> </li> <li> <p>We could now try to embed the image using <code>drawImage()</code>, but we need to make sure the image file has been loaded first, otherwise the code will fail. We can achieve this using the <code>load</code> event, which will only be fired when the image has finished loading. Add the following block below the previous one:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>image.addEventListener("load", () => ctx.drawImage(image, 20, 20)); </code></pre></div> <p>If you load your example in the browser now, you should see the image embedded in the canvas.</p> </li> <li> <p>But there's more! What if we want to display only a part of the image, or to resize it? We can do both with the more complex version of <code>drawImage()</code>. Update your <code>ctx.drawImage()</code> line like so:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.drawImage(image, 20, 20, 185, 175, 50, 50, 185, 175); </code></pre></div> <ul> <li>The first parameter is the image reference, as before.</li> <li>Parameters 2 and 3 define the coordinates of the top left corner of the area you want to cut out of the loaded image, relative to the top-left corner of the image itself. Nothing to the left of the first parameter or above the second will be drawn.</li> <li>Parameters 4 and 5 define the width and height of the area we want to cut out from the original image we loaded.</li> <li>Parameters 6 and 7 define the coordinates at which you want to draw the top-left corner of the cut-out portion of the image, relative to the top-left corner of the canvas.</li> <li>Parameters 8 and 9 define the width and height to draw the cut-out area of the image. In this case, we have specified the same dimensions as the original slice, but you could resize it by specifying different values.</li> </ul> </li> <li> <p>When the image is meaningfully updated, the <a href="/en-US/docs/Glossary/Accessible_description">accessible description</a> must also be updated.</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>canvas.setAttribute("aria-label", "Firefox Logo"); </code></pre></div> </li> </ol> <p>The final example should look like so:</p><iframe width="100%" height="260" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/5_canvas_images/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>Note:</strong> The finished code is available on GitHub as <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/5_canvas_images" class="external" target="_blank">5_canvas_images</a>.</p> </div></div></section><section aria-labelledby="loops_and_animations"><h2 id="loops_and_animations"><a href="#loops_and_animations">Loops and animations</a></h2><div class="section-content"><p>We have so far covered some very basic uses of 2D canvas, but really you won't experience the full power of canvas unless you update or animate it in some way. After all, canvas does provide scriptable images! If you aren't going to change anything, then you might as well just use static images and save yourself all the work.</p></div></section><section aria-labelledby="creating_a_loop"><h3 id="creating_a_loop"><a href="#creating_a_loop">Creating a loop</a></h3><div class="section-content"><p>Playing with loops in canvas is rather fun — you can run canvas commands inside a <a href="/en-US/docs/Web/JavaScript/Reference/Statements/for"><code>for</code></a> (or other type of) loop just like any other JavaScript code.</p> <p>Let's build an example.</p> <ol> <li> <p>Make another fresh copy of our canvas template (<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>) and open it in your code editor.</p> </li> <li> <p>Add the following line to the bottom of your JavaScript. This contains a new method, <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/translate" title="translate()"><code>translate()</code></a>, which moves the origin point of the canvas:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.translate(width / 2, height / 2); </code></pre></div> <p>This causes the coordinate origin (0, 0) to be moved to the center of the canvas, rather than being at the top left corner. This is very useful in many situations, like this one, where we want our design to be drawn relative to the center of the canvas.</p> </li> <li> <p>Now add the following code to the bottom of the JavaScript:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function degToRad(degrees) { return (degrees * Math.PI) / 180; } function rand(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } let length = 250; let moveOffset = 20; for (let i = 0; i < length; i++) {} </code></pre></div> <p>Here we are implementing the same <code>degToRad()</code> function we saw in the triangle example above, a <code>rand()</code> function that returns a random number between given lower and upper bounds, <code>length</code> and <code>moveOffset</code> variables (which we'll find out more about later), and an empty <code>for</code> loop.</p> </li> <li> <p>The idea here is that we'll draw something on the canvas inside the <code>for</code> loop, and iterate on it each time so we can create something interesting. Add the following code inside your <code>for</code> loop:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = `rgb(${255 - length} 0 ${255 - length} / 90%)`; ctx.beginPath(); ctx.moveTo(moveOffset, moveOffset); ctx.lineTo(moveOffset + length, moveOffset); const triHeight = (length / 2) * Math.tan(degToRad(60)); ctx.lineTo(moveOffset + length / 2, moveOffset + triHeight); ctx.lineTo(moveOffset, moveOffset); ctx.fill(); length--; moveOffset += 0.7; ctx.rotate(degToRad(5)); </code></pre></div> <p>So on each iteration, we:</p> <ul> <li>Set the <code>fillStyle</code> to be a shade of slightly transparent purple, which changes each time based on the value of <code>length</code>. As you'll see later the length gets smaller each time the loop runs, so the effect here is that the color gets brighter with each successive triangle drawn.</li> <li>Begin the path.</li> <li>Move the pen to a coordinate of <code>(moveOffset, moveOffset)</code>; This variable defines how far we want to move each time we draw a new triangle.</li> <li>Draw a line to a coordinate of <code>(moveOffset+length, moveOffset)</code>. This draws a line of length <code>length</code> parallel to the X axis.</li> <li>Calculate the triangle's height, as before.</li> <li>Draw a line to the downward-pointing corner of the triangle, then draw a line back to the start of the triangle.</li> <li>Call <code>fill()</code> to fill in the triangle.</li> <li>Update the variables that describe the sequence of triangles, so we can be ready to draw the next one. We decrease the <code>length</code> value by 1, so the triangles get smaller each time; increase <code>moveOffset</code> by a small amount so each successive triangle is slightly further away, and use another new function, <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/rotate" title="rotate()"><code>rotate()</code></a>, which allows us to rotate the entire canvas! We rotate it by 5 degrees before drawing the next triangle.</li> </ul> </li> </ol> <p>That's it! The final example should look like so:</p><iframe width="100%" height="550" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop/index.html" loading="lazy"></iframe> <p>At this point, we'd like to encourage you to play with the example and make it your own! For example:</p> <ul> <li>Draw rectangles or arcs instead of triangles, or even embed images.</li> <li>Play with the <code>length</code> and <code>moveOffset</code> values.</li> <li>Introduce some random numbers using that <code>rand()</code> function we included above but didn't use.</li> </ul> <div class="notecard note"> <p><strong>Note:</strong> The finished code is available on GitHub as <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop" class="external" target="_blank">6_canvas_for_loop</a>.</p> </div></div></section><section aria-labelledby="animations"><h3 id="animations"><a href="#animations">Animations</a></h3><div class="section-content"><p>The loop example we built above was fun, but really you need a constant loop that keeps going and going for any serious canvas applications (such as games and real time visualizations). If you think of your canvas as being like a movie, you really want the display to update on each frame to show the updated view, with an ideal refresh rate of 60 frames per second so that movement appears nice and smooth to the human eye.</p> <p>There are a few JavaScript functions that will allow you to run functions repeatedly, several times a second, the best one for our purposes here being <a href="/en-US/docs/Web/API/Window/requestAnimationFrame"><code>window.requestAnimationFrame()</code></a>. It takes one parameter — the name of the function you want to run for each frame. The next time the browser is ready to update the screen, your function will get called. If that function draws the new update to your animation, then calls <code>requestAnimationFrame()</code> again just before the end of the function, the animation loop will continue to run. The loop ends when you stop calling <code>requestAnimationFrame()</code> or if you call <a href="/en-US/docs/Web/API/Window/cancelAnimationFrame"><code>window.cancelAnimationFrame()</code></a> after calling <code>requestAnimationFrame()</code> but before the frame is called.</p> <div class="notecard note"> <p><strong>Note:</strong> It's good practice to call <code>cancelAnimationFrame()</code> from your main code when you're done using the animation, to ensure that no updates are still waiting to be run.</p> </div> <p>The browser works out complex details such as making the animation run at a consistent speed, and not wasting resources animating things that can't be seen.</p> <p>To see how it works, let's quickly look again at our Bouncing Balls example (<a href="https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html" class="external" target="_blank">see it live</a>, and also see <a href="https://github.com/mdn/learning-area/tree/main/javascript/oojs/bouncing-balls" class="external" target="_blank">the source code</a>). The code for the loop that keeps everything moving looks like this:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function loop() { ctx.fillStyle = "rgb(0 0 0 / 25%)"; ctx.fillRect(0, 0, width, height); for (const ball of balls) { ball.draw(); ball.update(); ball.collisionDetect(); } requestAnimationFrame(loop); } loop(); </code></pre></div> <p>We run the <code>loop()</code> function once at the bottom of the code to start the cycle, drawing the first animation frame; the <code>loop()</code> function then takes charge of calling <code>requestAnimationFrame(loop)</code> to run the next frame of the animation, again and again.</p> <p>Note that on each frame we are completely clearing the canvas and redrawing everything. For every ball present we draw it, update its position, and check to see if it is colliding with any other balls. Once you've drawn a graphic to a canvas, there's no way to manipulate that graphic individually like you can with DOM elements. You can't move each ball around on the canvas, because once it's drawn, it's part of the canvas, and is not an individual accessible element or object. Instead, you have to erase and redraw, either by erasing the entire frame and redrawing everything, or by having code that knows exactly what parts need to be erased and only erases and redraws the minimum area of the canvas necessary.</p> <p>Optimizing animation of graphics is an entire specialty of programming, with lots of clever techniques available. Those are beyond what we need for our example, though!</p> <p>In general, the process of doing a canvas animation involves the following steps:</p> <ol> <li>Clear the canvas contents (e.g. with <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fillRect" title="fillRect()"><code>fillRect()</code></a> or <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/clearRect" title="clearRect()"><code>clearRect()</code></a>).</li> <li>Save state (if necessary) using <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/save" title="save()"><code>save()</code></a> — this is needed when you want to save settings you've updated on the canvas before continuing, which is useful for more advanced applications.</li> <li>Draw the graphics you are animating.</li> <li>Restore the settings you saved in step 2, using <a href="/en-US/docs/Web/API/CanvasRenderingContext2D/restore" title="restore()"><code>restore()</code></a></li> <li>Call <code>requestAnimationFrame()</code> to schedule drawing of the next frame of the animation.</li> </ol> <div class="notecard note"> <p><strong>Note:</strong> We won't cover <code>save()</code> and <code>restore()</code> here, but they are explained nicely in our <a href="/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations">Transformations</a> tutorial (and the ones that follow it).</p> </div></div></section><section aria-labelledby="a_simple_character_animation"><h3 id="a_simple_character_animation"><a href="#a_simple_character_animation">A simple character animation</a></h3><div class="section-content"><p>Now let's create our own simple animation — we'll get a character from a certain rather awesome retro computer game to walk across the screen.</p> <ol> <li> <p>Make another fresh copy of our canvas template (<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>) and open it in your code editor.</p> </li> <li> <p>Update the inner HTML to reflect the image:</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code><canvas class="myCanvas"> <p>A man walking.</p> </canvas> </code></pre></div> </li> <li> <p>At the bottom of the JavaScript, add the following line to once again make the coordinate origin sit in the middle of the canvas:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.translate(width / 2, height / 2); </code></pre></div> </li> <li> <p>Now let's create a new <a href="/en-US/docs/Web/API/HTMLImageElement"><code>HTMLImageElement</code></a> object, set its <a href="/en-US/docs/Web/HTML/Element/img#src"><code>src</code></a> to the image we want to load, and add an <code>onload</code> event handler that will cause the <code>draw()</code> function to fire when the image is loaded:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const image = new Image(); image.src = "walk-right.png"; image.onload = draw; </code></pre></div> </li> <li> <p>Now we'll add some variables to keep track of the position the sprite is to be drawn on the screen, and the sprite number we want to display.</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let sprite = 0; let posX = 0; </code></pre></div> <p>Let's explain the spritesheet image (which we have respectfully borrowed from Mike Thomas' <a href="https://codepen.io/mikethomas/pen/kQjKLW" class="external" target="_blank">Walking cycle using CSS animation</a> CodePen). The image looks like this:</p> <p> <img src="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics/walk-right.png" alt="A sprite sheet with six sprite images of a pixelated character resembling a walking person from their right side at different instances of a single step forward. The character has a white shirt with sky blue buttons, black trousers, and black shoes. Each sprite is 102 pixels wide and 148 pixels high." width="612" height="148" loading="lazy"> </p> <p>It contains six sprites that make up the whole walking sequence — each one is 102 pixels wide and 148 pixels high. To display each sprite cleanly we will have to use <code>drawImage()</code> to chop out a single sprite image from the spritesheet and display only that part, like we did above with the Firefox logo. The X coordinate of the slice will have to be a multiple of 102, and the Y coordinate will always be 0. The slice size will always be 102 by 148 pixels.</p> </li> <li> <p>Now let's insert an empty <code>draw()</code> function at the bottom of the code, ready for filling up with some code:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function draw() {} </code></pre></div> </li> <li> <p>The rest of the code in this section goes inside <code>draw()</code>. First, add the following line, which clears the canvas to prepare for drawing each frame. Notice that we have to specify the top-left corner of the rectangle as <code>-(width/2), -(height/2)</code> because we specified the origin position as <code>width/2, height/2</code> earlier on.</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillRect(-(width / 2), -(height / 2), width, height); </code></pre></div> </li> <li> <p>Next, we'll draw our image using drawImage — the 9-parameter version. Add the following:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.drawImage(image, sprite * 102, 0, 102, 148, 0 + posX, -74, 102, 148); </code></pre></div> <p>As you can see:</p> <ul> <li>We specify <code>image</code> as the image to embed.</li> <li>Parameters 2 and 3 specify the top-left corner of the slice to cut out of the source image, with the X value as <code>sprite</code> multiplied by 102 (where <code>sprite</code> is the sprite number between 0 and 5) and the Y value always 0.</li> <li>Parameters 4 and 5 specify the size of the slice to cut out — 102 pixels by 148 pixels.</li> <li>Parameters 6 and 7 specify the top-left corner of the box into which to draw the slice on the canvas — the X position is 0 + <code>posX</code>, meaning that we can alter the drawing position by altering the <code>posX</code> value.</li> <li>Parameters 8 and 9 specify the size of the image on the canvas. We just want to keep its original size, so we specify 102 and 148 as the width and height.</li> </ul> </li> <li> <p>Now we'll alter the <code>sprite</code> value after each draw — well, after some of them anyway. Add the following block to the bottom of the <code>draw()</code> function:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>if (posX % 13 === 0) { if (sprite === 5) { sprite = 0; } else { sprite++; } } </code></pre></div> <p>We are wrapping the whole block in <code>if (posX % 13 === 0) { }</code>. We use the modulo (<code>%</code>) operator (also known as the <a href="/en-US/docs/Web/JavaScript/Reference/Operators/Remainder">remainder operator</a>) to check whether the <code>posX</code> value can be exactly divided by 13 with no remainder. If so, we move on to the next sprite by incrementing <code>sprite</code> (wrapping to 0 after we're done with sprite #5). This effectively means that we are only updating the sprite on every 13th frame, or roughly about 5 frames a second (<code>requestAnimationFrame()</code> calls us at up to 60 frames per second if possible). We are deliberately slowing down the frame rate because we only have six sprites to work with, and if we display one every 60th of a second, our character will move way too fast!</p> <p>Inside the outer block we use an <a href="/en-US/docs/Web/JavaScript/Reference/Statements/if...else"><code>if...else</code></a> statement to check whether the <code>sprite</code> value is at 5 (the last sprite, given that the sprite numbers run from 0 to 5). If we are showing the last sprite already, we reset <code>sprite</code> back to 0; if not we just increment it by 1.</p> </li> <li> <p>Next we need to work out how to change the <code>posX</code> value on each frame — add the following code block just below your last one.</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>if (posX > width / 2) { let newStartPos = -(width / 2 + 102); posX = Math.ceil(newStartPos); console.log(posX); } else { posX += 2; } </code></pre></div> <p>We are using another <code>if...else</code> statement to see if the value of <code>posX</code> has become greater than <code>width/2</code>, which means our character has walked off the right edge of the screen. If so, we calculate a position that would put the character just to the left of the left side of the screen.</p> <p>If our character hasn't yet walked off the edge of the screen, we increment <code>posX</code> by 2. This will make him move a little bit to the right the next time we draw him.</p> </li> <li> <p>Finally, we need to make the animation loop by calling <a href="/en-US/docs/Web/API/Window/requestAnimationFrame" title="requestAnimationFrame()"><code>requestAnimationFrame()</code></a> at the bottom of the <code>draw()</code> function:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>window.requestAnimationFrame(draw); </code></pre></div> </li> </ol> <p>That's it! The final example should look like so:</p><iframe width="100%" height="260" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>Note:</strong> The finished code is available on GitHub as <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation" class="external" target="_blank">7_canvas_walking_animation</a>.</p> </div></div></section><section aria-labelledby="a_simple_drawing_application"><h3 id="a_simple_drawing_application"><a href="#a_simple_drawing_application">A simple drawing application</a></h3><div class="section-content"><p>As a final animation example, we'd like to show you a very simple drawing application, to illustrate how the animation loop can be combined with user input (like mouse movement, in this case). We won't get you to walk through and build this one; we'll just explore the most interesting parts of the code.</p> <p>The example can be found on GitHub as <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app" class="external" target="_blank">8_canvas_drawing_app</a>, and you can play with it live below:</p><iframe width="100%" height="600" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app/index.html" loading="lazy"></iframe> <p>Let's look at the most interesting parts. First of all, we keep track of the mouse's X and Y coordinates and whether it is being clicked or not with three variables: <code>curX</code>, <code>curY</code>, and <code>pressed</code>. When the mouse moves, we fire a function set as the <code>onmousemove</code> event handler, which captures the current X and Y values. We also use <code>onmousedown</code> and <code>onmouseup</code> event handlers to change the value of <code>pressed</code> to <code>true</code> when the mouse button is pressed, and back to <code>false</code> again when it is released.</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let curX; let curY; let pressed = false; // update mouse pointer coordinates document.addEventListener("mousemove", (e) => { curX = e.pageX; curY = e.pageY; }); canvas.addEventListener("mousedown", () => (pressed = true)); canvas.addEventListener("mouseup", () => (pressed = false)); </code></pre></div> <p>When the "Clear canvas" button is pressed, we run a simple function that clears the whole canvas back to black, the same way we've seen before:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>clearBtn.addEventListener("click", () => { ctx.fillStyle = "rgb(0 0 0)"; ctx.fillRect(0, 0, width, height); }); </code></pre></div> <p>The drawing loop is pretty simple this time around — if pressed is <code>true</code>, we draw a circle with a fill style equal to the value in the color picker, and a radius equal to the value set in the range input. We have to draw the circle 85 pixels above where we measured it from, because the vertical measurement is taken from the top of the viewport, but we are drawing the circle relative to the top of the canvas, which starts below the 85 pixel-high toolbar. If we drew it with just <code>curY</code> as the y coordinate, it would appear 85 pixels lower than the mouse position.</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function draw() { if (pressed) { ctx.fillStyle = colorPicker.value; ctx.beginPath(); ctx.arc( curX, curY - 85, sizePicker.value, degToRad(0), degToRad(360), false, ); ctx.fill(); } requestAnimationFrame(draw); } draw(); </code></pre></div> <p>All <a href="/en-US/docs/Web/HTML/Element/input"><code><input></code></a> types are well supported. If a browser doesn't support an input type, it will fall back to a plain text fields.</p></div></section><section aria-labelledby="webgl"><h2 id="webgl"><a href="#webgl">WebGL</a></h2><div class="section-content"><p>It's now time to leave 2D behind, and take a quick look at 3D canvas. 3D canvas content is specified using the <a href="/en-US/docs/Web/API/WebGL_API">WebGL API</a>, which is a completely separate API from the 2D canvas API, even though they both render onto <a href="/en-US/docs/Web/HTML/Element/canvas"><code><canvas></code></a> elements.</p> <p>WebGL is based on <a href="/en-US/docs/Glossary/OpenGL">OpenGL</a> (Open Graphics Library), and allows you to communicate directly with the computer's <a href="/en-US/docs/Glossary/GPU">GPU</a>. As such, writing raw WebGL is closer to low level languages such as C++ than regular JavaScript; it is quite complex but incredibly powerful.</p></div></section><section aria-labelledby="using_a_library"><h3 id="using_a_library"><a href="#using_a_library">Using a library</a></h3><div class="section-content"><p>Because of its complexity, most people write 3D graphics code using a third party JavaScript library such as <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Three.js</a>, <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas">PlayCanvas</a>, or <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js">Babylon.js</a>. Most of these work in a similar way, providing functionality to create primitive and custom shapes, position viewing cameras and lighting, covering surfaces with textures, and more. They handle the WebGL for you, letting you work on a higher level.</p> <p>Yes, using one of these means learning another new API (a third party one, in this case), but they are a lot simpler than coding raw WebGL.</p></div></section><section aria-labelledby="recreating_our_cube"><h3 id="recreating_our_cube"><a href="#recreating_our_cube">Recreating our cube</a></h3><div class="section-content"><p>Let's look at an example of how to create something with a WebGL library. We'll choose <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Three.js</a>, as it is one of the most popular ones. In this tutorial we'll create the 3D spinning cube we saw earlier.</p> <ol> <li> <p>To start with, make a local copy of <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/index.html" class="external" target="_blank">threejs-cube/index.html</a> in a new folder, then save a copy of <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/metal003.png" class="external" target="_blank">metal003.png</a> in the same folder. This is the image we'll use as a surface texture for the cube later on.</p> </li> <li> <p>Next, create a new file called <code>script.js</code>, again in the same folder as before.</p> </li> <li> <p>Next, you need to have the Three.js library installed. You can follow the environment setup steps described in the <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Building up a basic demo with Three.js</a> so that you have Three.js working as expected.</p> </li> <li> <p>Now we've got <code>three.js</code> attached to our page, we can start to write JavaScript that makes use of it into <code>script.js</code>. Let's start by creating a new scene — add the following into your <code>script.js</code> file:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const scene = new THREE.Scene(); </code></pre></div> <p>The <a href="https://threejs.org/docs/index.html#api/en/scenes/Scene" class="external" target="_blank"><code>Scene()</code></a> constructor creates a new scene, which represents the whole 3D world we are trying to display.</p> </li> <li> <p>Next, we need a <strong>camera</strong> so we can see the scene. In 3D imagery terms, the camera represents a viewer's position in the world. To create a camera, add the following lines next:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000, ); camera.position.z = 5; </code></pre></div> <p>The <a href="https://threejs.org/docs/index.html#api/en/cameras/PerspectiveCamera" class="external" target="_blank"><code>PerspectiveCamera()</code></a> constructor takes four arguments:</p> <ul> <li>The field of view: How wide the area in front of the camera is that should be visible onscreen, in degrees.</li> <li>The <a href="/en-US/docs/Glossary/Aspect_ratio">aspect ratio</a>: Usually, this is the ratio of the scene's width divided by the scene's height. Using another value will distort the scene (which might be what you want, but usually isn't).</li> <li>The near plane: How close to the camera objects can be before we stop rendering them to the screen. Think about how when you move your fingertip closer and closer to the space between your eyes, eventually you can't see it anymore.</li> <li>The far plane: How far away things are from the camera before they are no longer rendered.</li> </ul> <p>We also set the camera's position to be 5 distance units out of the Z axis, which, like in CSS, is out of the screen towards you, the viewer.</p> </li> <li> <p>The third vital ingredient is a renderer. This is an object that renders a given scene, as viewed through a given camera. We'll create one for now using the <a href="https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderer" class="external" target="_blank"><code>WebGLRenderer()</code></a> constructor, but we'll not use it till later. Add the following lines next:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); </code></pre></div> <p>The first line creates a new renderer, the second line sets the size at which the renderer will draw the camera's view, and the third line appends the <a href="/en-US/docs/Web/HTML/Element/canvas"><code><canvas></code></a> element created by the renderer to the document's <a href="/en-US/docs/Web/HTML/Element/body"><code><body></code></a>. Now anything the renderer draws will be displayed in our window.</p> </li> <li> <p>Next, we want to create the cube we'll display on the canvas. Add the following chunk of code at the bottom of your JavaScript:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let cube; const loader = new THREE.TextureLoader(); loader.load("metal003.png", (texture) => { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(2, 2); const geometry = new THREE.BoxGeometry(2.4, 2.4, 2.4); const material = new THREE.MeshLambertMaterial({ map: texture }); cube = new THREE.Mesh(geometry, material); scene.add(cube); draw(); }); </code></pre></div> <p>There's a bit more to take in here, so let's go through it in stages:</p> <ul> <li>We first create a <code>cube</code> global variable so we can access our cube from anywhere in the code.</li> <li>Next, we create a new <a href="https://threejs.org/docs/index.html#api/en/loaders/TextureLoader" class="external" target="_blank"><code>TextureLoader</code></a> object, then call <code>load()</code> on it. <code>load()</code> takes two parameters in this case (although it can take more): the texture we want to load (our PNG), and a function that will run when the texture has loaded.</li> <li>Inside this function we use properties of the <a href="https://threejs.org/docs/index.html#api/en/textures/Texture" class="external" target="_blank"><code>texture</code></a> object to specify that we want a 2 x 2 repeat of the image wrapped around all sides of the cube. Next, we create a new <a href="https://threejs.org/docs/index.html#api/en/geometries/BoxGeometry" class="external" target="_blank"><code>BoxGeometry</code></a> object and a new <a href="https://threejs.org/docs/index.html#api/en/materials/MeshLambertMaterial" class="external" target="_blank"><code>MeshLambertMaterial</code></a> object, and bring them together in a <a href="https://threejs.org/docs/index.html#api/en/objects/Mesh" class="external" target="_blank"><code>Mesh</code></a> to create our cube. An object typically requires a geometry (what shape it is) and a material (what its surface looks like).</li> <li>Last of all, we add our cube to the scene, then call our <code>draw()</code> function to start off the animation.</li> </ul> </li> <li> <p>Before we get to defining <code>draw()</code>, we'll add a couple of lights to the scene, to liven things up a bit; add the following blocks next:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const light = new THREE.AmbientLight("rgb(255 255 255)"); // soft white light scene.add(light); const spotLight = new THREE.SpotLight("rgb(255 255 255)"); spotLight.position.set(100, 1000, 1000); spotLight.castShadow = true; scene.add(spotLight); </code></pre></div> <p>An <a href="https://threejs.org/docs/index.html#api/en/lights/AmbientLight" class="external" target="_blank"><code>AmbientLight</code></a> object is a kind of soft light that lightens the whole scene a bit, like the sun when you are outside. The <a href="https://threejs.org/docs/index.html#api/en/lights/SpotLight" class="external" target="_blank"><code>SpotLight</code></a> object, on the other hand, is a directional beam of light, more like a flashlight/torch (or a spotlight, in fact).</p> </li> <li> <p>Last of all, let's add our <code>draw()</code> function to the bottom of the code:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function draw() { cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera); requestAnimationFrame(draw); } </code></pre></div> <p>This is fairly intuitive; on each frame, we rotate our cube slightly on its X and Y axes, then render the scene as viewed by our camera, then finally call <code>requestAnimationFrame()</code> to schedule drawing our next frame.</p> </li> </ol> <p>Let's have another quick look at what the finished product should look like:</p><iframe width="100%" height="500" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html" loading="lazy"></iframe> <p>You can <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-cube" class="external" target="_blank">find the finished code on GitHub</a>.</p> <div class="notecard note"> <p><strong>Note:</strong> In our GitHub repo you can also find another interesting 3D cube example — <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-video-cube" class="external" target="_blank">Three.js Video Cube</a> (<a href="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-video-cube/" class="external" target="_blank">see it live also</a>). This uses <a href="/en-US/docs/Web/API/MediaDevices/getUserMedia" title="getUserMedia()"><code>getUserMedia()</code></a> to take a video stream from a computer web cam and project it onto the side of the cube as a texture!</p> </div></div></section><section aria-labelledby="summary"><h2 id="summary"><a href="#summary">Summary</a></h2><div class="section-content"><p>At this point, you should have a useful idea of the basics of graphics programming using Canvas and WebGL and what you can do with these APIs, as well as a good idea of where to go for further information. Have fun!</p></div></section><section aria-labelledby="see_also"><h2 id="see_also"><a href="#see_also">See also</a></h2><div class="section-content"><p>Here we have covered only the real basics of canvas — there is so much more to learn! The below articles will take you further.</p> <ul> <li><a href="/en-US/docs/Web/API/Canvas_API/Tutorial">Canvas tutorial</a> — A very detailed tutorial series explaining what you should know about 2D canvas in much more detail than was covered here. Essential reading.</li> <li><a href="/en-US/docs/Web/API/WebGL_API/Tutorial">WebGL tutorial</a> — A series that teaches the basics of raw WebGL programming.</li> <li><a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Building up a basic demo with Three.js</a> — basic Three.js tutorial. We also have equivalent guides for <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas">PlayCanvas</a> or <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js">Babylon.js</a>.</li> <li><a href="/en-US/docs/Games">Game development</a> — the landing page for web games development on MDN. There are some really useful tutorials and techniques available here related to 2D and 3D canvas — see the Techniques and Tutorials menu options.</li> </ul></div></section><section aria-labelledby="examples"><h2 id="examples"><a href="#examples">Examples</a></h2><div class="section-content"><ul> <li><a href="https://github.com/mdn/webaudio-examples/tree/main/violent-theremin" class="external" target="_blank">Violent theremin</a> — Uses the Web Audio API to generate sound, and canvas to generate a pretty visualization to go along with it.</li> <li><a href="https://github.com/mdn/webaudio-examples/tree/main/voice-change-o-matic" class="external" target="_blank">Voice change-o-matic</a> — Uses a canvas to visualize real-time audio data from the Web Audio API.</li> </ul><ul class="prev-next"> <li><a class="button secondary" href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs"><span class="button-wrap"> Previous </span></a></li> <li><a class="button secondary" href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs"><span class="button-wrap"> Overview: Client-side web APIs</span></a></li> <li><a class="button secondary" href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs"><span class="button-wrap"> Next </span></a></li> </ul></div></section></article><aside class="article-footer"><div class="article-footer-inner"><div class="svg-container"><svg xmlns="http://www.w3.org/2000/svg" width="162" height="162" viewBox="0 0 162 162" fill="none" role="none"><mask id="b" fill="#fff"><path d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z"></path></mask><path stroke="url(#a)" stroke-dasharray="6, 6" stroke-width="2" d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z" mask="url(#b)" style="stroke:url(#a)" transform="translate(-63.992 -25.587)"></path><ellipse cx="8.066" cy="111.597" fill="var(--background-tertiary)" rx="53.677" ry="53.699" transform="matrix(.71707 -.697 .7243 .6895 0 0)"></ellipse><g clip-path="url(#c)" transform="translate(-63.992 -25.587)"><path fill="#9abff5" d="m144.256 137.379 32.906 12.434a4.41 4.41 0 0 1 2.559 5.667l-9.326 24.679a4.41 4.41 0 0 1-5.667 2.559l-8.226-3.108-2.332 6.17c-.466 1.233-.375 1.883-1.609 1.417l-2.253-.527c-.411-.155-.95-.594-1.206-1.161l-4.734-10.484-12.545-4.741a4.41 4.41 0 0 1-2.559-5.667l9.325-24.679a4.41 4.41 0 0 1 5.667-2.559m9.961 29.617 8.227 3.108 3.264-8.638-.498-6.768-4.113-1.555.548 7.258-4.319-1.632zm-12.339-4.663 8.226 3.108 3.264-8.637-.498-6.769-4.113-1.554.548 7.257-4.319-1.632z"></path></g><g clip-path="url(#d)" transform="translate(-63.992 -25.587)"><path fill="#81b0f3" d="M135.35 60.136 86.67 41.654c-3.346-1.27-7.124.428-8.394 3.775L64.414 81.938c-1.27 3.347.428 7.125 3.774 8.395l12.17 4.62-3.465 9.128c-.693 1.826-1.432 2.457.394 3.15l3.014 1.625c.609.231 1.637.274 2.477-.104l15.53-6.983 18.56 7.047c3.346 1.27 7.124-.428 8.395-3.775l13.862-36.51c1.27-3.346-.428-7.124-3.775-8.395M95.261 83.207l-12.17-4.62 4.852-12.779 7.19-7.017 6.085 2.31-7.725 7.51 6.389 2.426zm18.255 6.93-12.17-4.62 4.852-12.778 7.189-7.017 6.085 2.31-7.725 7.51 6.39 2.426z"></path></g><defs><clipPath id="c"><path fill="#fff" d="m198.638 146.586-65.056-24.583-24.583 65.057 65.056 24.582z"></path></clipPath><clipPath id="d"><path fill="#fff" d="m66.438 14.055 96.242 36.54-36.54 96.243-96.243-36.54z"></path></clipPath><linearGradient id="a" x1="97.203" x2="199.995" y1="47.04" y2="152.793" gradientUnits="userSpaceOnUse"><stop stop-color="#086DFC"></stop><stop offset="0.246" stop-color="#2C81FA"></stop><stop offset="0.516" stop-color="#5497F8"></stop><stop offset="0.821" stop-color="#80B0F6"></stop><stop offset="1" stop-color="#9ABFF5"></stop></linearGradient></defs></svg></div><h2>Help improve MDN</h2><fieldset class="feedback"><label>Was this page helpful to you?</label><div class="button-container"><button type="button" class="button primary has-icon yes"><span class="button-wrap"><span class="icon icon-thumbs-up "></span>Yes</span></button><button type="button" class="button primary has-icon no"><span class="button-wrap"><span class="icon icon-thumbs-down "></span>No</span></button></div></fieldset><a class="contribute" href="https://github.com/mdn/content/blob/main/CONTRIBUTING.md" title="This will take you to our contribution guidelines on GitHub." target="_blank" rel="noopener noreferrer">Learn how to contribute</a>.<p class="last-modified-date">This page was last modified on<!-- --> <time dateTime="2024-11-21T19:49:47.000Z">Nov 21, 2024</time> by<!-- --> <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics/contributors.txt" rel="nofollow">MDN contributors</a>.</p><div id="on-github" class="on-github"><a href="https://github.com/mdn/content/blob/main/files/en-us/learn/javascript/client-side_web_apis/drawing_graphics/index.md?plain=1" title="Folder: en-us/learn/javascript/client-side_web_apis/drawing_graphics (Opens in a new tab)" target="_blank" rel="noopener noreferrer">View this page on GitHub</a> <!-- -->•<!-- --> <a href="https://github.com/mdn/content/issues/new?template=page-report.yml&mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FLearn%2FJavaScript%2FClient-side_web_APIs%2FDrawing_graphics&metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EPage+report+details%3C%2Fsummary%3E%0A%0A*+Folder%3A+%60en-us%2Flearn%2Fjavascript%2Fclient-side_web_apis%2Fdrawing_graphics%60%0A*+MDN+URL%3A+https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FLearn%2FJavaScript%2FClient-side_web_APIs%2FDrawing_graphics%0A*+GitHub+URL%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Fcontent%2Fblob%2Fmain%2Ffiles%2Fen-us%2Flearn%2Fjavascript%2Fclient-side_web_apis%2Fdrawing_graphics%2Findex.md%0A*+Last+commit%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Fcontent%2Fcommit%2F93b34fcdb9cf91ff44f5dfe7f4dcd13e961962da%0A*+Document+last+modified%3A+2024-11-21T19%3A49%3A47.000Z%0A%0A%3C%2Fdetails%3E" title="This will take you to GitHub to file a new issue." target="_blank" rel="noopener noreferrer">Report a problem with this content</a></div></div></aside></main></div></div><footer id="nav-footer" class="page-footer"><div class="page-footer-grid"><div class="page-footer-logo-col"><a href="/" class="mdn-footer-logo" aria-label="MDN homepage"><svg width="48" height="17" viewBox="0 0 48 17" fill="none" xmlns="http://www.w3.org/2000/svg"><title id="mdn-footer-logo-svg">MDN logo</title><path d="M20.04 16.512H15.504V10.416C15.504 9.488 15.344 8.824 15.024 8.424C14.72 8.024 14.264 7.824 13.656 7.824C12.92 7.824 12.384 8.064 12.048 8.544C11.728 9.024 11.568 9.64 11.568 10.392V14.184H13.008V16.512H8.472V10.416C8.472 9.488 8.312 8.824 7.992 8.424C7.688 8.024 7.232 7.824 6.624 7.824C5.872 7.824 5.336 8.064 5.016 8.544C4.696 9.024 4.536 9.64 4.536 10.392V14.184H6.6V16.512H0V14.184H1.44V8.04H0.024V5.688H4.536V7.32C5.224 6.088 6.32 5.472 7.824 5.472C8.608 5.472 9.328 5.664 9.984 6.048C10.64 6.432 11.096 7.016 11.352 7.8C11.992 6.248 13.168 5.472 14.88 5.472C15.856 5.472 16.72 5.776 17.472 6.384C18.224 6.992 18.6 7.936 18.6 9.216V14.184H20.04V16.512Z" fill="currentColor"></path><path d="M33.6714 16.512H29.1354V14.496C28.8314 15.12 28.3834 15.656 27.7914 16.104C27.1994 16.536 26.4154 16.752 25.4394 16.752C24.0154 16.752 22.8954 16.264 22.0794 15.288C21.2634 14.312 20.8554 12.984 20.8554 11.304C20.8554 9.688 21.2554 8.312 22.0554 7.176C22.8554 6.04 24.0634 5.472 25.6794 5.472C26.5594 5.472 27.2794 5.648 27.8394 6C28.3994 6.352 28.8314 6.8 29.1354 7.344V2.352H26.9754V0H32.2314V14.184H33.6714V16.512ZM29.1354 11.04V10.776C29.1354 9.88 28.8954 9.184 28.4154 8.688C27.9514 8.176 27.3674 7.92 26.6634 7.92C25.9754 7.92 25.3674 8.176 24.8394 8.688C24.3274 9.2 24.0714 10.008 24.0714 11.112C24.0714 12.152 24.3114 12.944 24.7914 13.488C25.2714 14.032 25.8394 14.304 26.4954 14.304C27.3114 14.304 27.9514 13.96 28.4154 13.272C28.8954 12.584 29.1354 11.84 29.1354 11.04Z" fill="currentColor"></path><path d="M47.9589 16.512H41.9829V14.184H43.4229V10.416C43.4229 9.488 43.2629 8.824 42.9429 8.424C42.6389 8.024 42.1829 7.824 41.5749 7.824C40.8389 7.824 40.2709 8.056 39.8709 8.52C39.4709 8.968 39.2629 9.56 39.2469 10.296V14.184H40.6869V16.512H34.7109V14.184H36.1509V8.04H34.5909V5.688H39.2469V7.344C39.9669 6.096 41.1269 5.472 42.7269 5.472C43.7509 5.472 44.6389 5.776 45.3909 6.384C46.1429 6.992 46.5189 7.936 46.5189 9.216V14.184H47.9589V16.512Z" fill="currentColor"></path></svg></a><p>Your blueprint for a better internet.</p><ul class="social-icons"><li><a href="https://mozilla.social/@mdn" target="_blank" rel="me noopener noreferrer"><span class="icon icon-mastodon"></span><span class="visually-hidden">MDN on Mastodon</span></a></li><li><a href="https://twitter.com/mozdevnet" target="_blank" rel="noopener noreferrer"><span class="icon icon-twitter-x"></span><span class="visually-hidden">MDN on X (formerly Twitter)</span></a></li><li><a href="https://github.com/mdn/" target="_blank" rel="noopener noreferrer"><span class="icon icon-github-mark-small"></span><span class="visually-hidden">MDN on GitHub</span></a></li><li><a href="/en-US/blog/rss.xml" target="_blank"><span class="icon icon-feed"></span><span class="visually-hidden">MDN Blog RSS Feed</span></a></li></ul></div><div class="page-footer-nav-col-1"><h2 class="footer-nav-heading">MDN</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a href="/en-US/about">About</a></li><li class="footer-nav-item"><a href="/en-US/blog/">Blog</a></li><li class="footer-nav-item"><a href="https://www.mozilla.org/en-US/careers/listings/?team=ProdOps" target="_blank" rel="noopener noreferrer">Careers</a></li><li class="footer-nav-item"><a href="/en-US/advertising">Advertise with us</a></li></ul></div><div class="page-footer-nav-col-2"><h2 class="footer-nav-heading">Support</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="https://support.mozilla.org/products/mdn-plus">Product help</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/MDN/Community/Issues">Report an issue</a></li></ul></div><div class="page-footer-nav-col-3"><h2 class="footer-nav-heading">Our communities</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/community">MDN Community</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="https://discourse.mozilla.org/c/mdn/236" target="_blank" rel="noopener noreferrer">MDN Forum</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/discord" target="_blank" rel="noopener noreferrer">MDN Chat</a></li></ul></div><div class="page-footer-nav-col-4"><h2 class="footer-nav-heading">Developers</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/Web">Web Technologies</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/docs/Learn">Learn Web Development</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/plus">MDN Plus</a></li><li class="footer-nav-item"><a href="https://hacks.mozilla.org/" target="_blank" rel="noopener noreferrer">Hacks Blog</a></li></ul></div><div class="page-footer-moz"><a href="https://www.mozilla.org/" class="footer-moz-logo-link" target="_blank" rel="noopener noreferrer"><svg width="112" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><title id="mozilla-footer-logo-svg">Mozilla logo</title><path d="M41.753 14.218c-2.048 0-3.324 1.522-3.324 4.157 0 2.423 1.119 4.286 3.29 4.286 2.082 0 3.447-1.678 3.447-4.347 0-2.826-1.522-4.096-3.413-4.096Zm54.89 7.044c0 .901.437 1.618 1.645 1.618 1.427 0 2.949-1.024 3.044-3.352-.649-.095-1.365-.185-2.02-.185-1.426-.005-2.668.397-2.668 1.92Z" fill="currentColor"></path><path d="M0 0v32h111.908V0H0Zm32.56 25.426h-5.87v-7.884c0-2.423-.806-3.352-2.39-3.352-1.924 0-2.702 1.365-2.702 3.324v4.868h1.864v3.044h-5.864v-7.884c0-2.423-.806-3.352-2.39-3.352-1.924 0-2.702 1.365-2.702 3.324v4.868h2.669v3.044H6.642v-3.044h1.863v-7.918H6.642V11.42h5.864v2.11c.839-1.489 2.3-2.39 4.252-2.39 2.02 0 3.878.963 4.566 3.01.778-1.862 2.361-3.01 4.566-3.01 2.512 0 4.812 1.522 4.812 4.84v6.402h1.863v3.044h-.005Zm9.036.307c-4.314 0-7.296-2.635-7.296-7.106 0-4.096 2.484-7.481 7.514-7.481s7.481 3.38 7.481 7.29c0 4.472-3.228 7.297-7.699 7.297Zm22.578-.307H51.942l-.403-2.11 7.7-8.846h-4.376l-.621 2.17-2.888-.313.498-4.907h12.294l.313 2.11-7.767 8.852h4.533l.654-2.172 3.167.308-.872 4.908Zm7.99 0h-4.191v-5.03h4.19v5.03Zm0-8.976h-4.191v-5.03h4.19v5.03Zm2.618 8.976 6.054-21.358h3.945l-6.054 21.358h-3.945Zm8.136 0 6.048-21.358h3.945l-6.054 21.358h-3.939Zm21.486.307c-1.863 0-2.887-1.085-3.072-2.792-.805 1.427-2.232 2.792-4.498 2.792-2.02 0-4.314-1.085-4.314-4.006 0-3.447 3.323-4.253 6.518-4.253.778 0 1.584.034 2.3.124v-.465c0-1.427-.034-3.133-2.3-3.133-.84 0-1.488.061-2.143.402l-.453 1.578-3.195-.34.549-3.224c2.45-.996 3.692-1.27 5.992-1.27 3.01 0 5.556 1.55 5.556 4.75v6.083c0 .805.314 1.085.963 1.085.184 0 .375-.034.587-.095l.034 2.11a5.432 5.432 0 0 1-2.524.654Z" fill="currentColor"></path></svg></a><ul class="footer-moz-list"><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Website Privacy Notice</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/#cookies" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Cookies</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/legal/terms/mozilla" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Legal</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/governance/policies/participation/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Community Participation Guidelines</a></li></ul></div><div class="page-footer-legal"><p id="license" class="page-footer-legal-text">Visit<!-- --> <a href="https://www.mozilla.org" target="_blank" rel="noopener noreferrer">Mozilla Corporation’s</a> <!-- -->not-for-profit parent, the<!-- --> <a target="_blank" rel="noopener noreferrer" href="https://foundation.mozilla.org/">Mozilla Foundation</a>.<br/>Portions of this content are ©1998–<!-- -->2024<!-- --> by individual mozilla.org contributors. Content available under<!-- --> <a href="/en-US/docs/MDN/Writing_guidelines/Attrib_copyright_license">a Creative Commons license</a>.</p></div></div></footer></div><script type="application/json" id="hydration">{"url":"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics","doc":{"isMarkdown":true,"isTranslated":false,"isActive":true,"flaws":{},"title":"Drawing graphics","mdn_url":"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics","locale":"en-US","native":"English (US)","sidebarHTML":"<ol><li class=\"section\"><a href=\"/en-US/docs/Learn/Getting_started_with_the_web\">Complete beginners start here!</a></li><li><details><summary>Getting started with the web</summary><ol><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web\">Getting started with the web</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/Installing_basic_software\">Installing basic software</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/What_will_your_website_look_like\">What will your website look like?</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/Dealing_with_files\">Dealing with files</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics\">HTML basics</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/CSS_basics\">CSS basics</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics\">JavaScript basics</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/Publishing_your_website\">Publishing your website</a></li><li><a href=\"/en-US/docs/Learn/Getting_started_with_the_web/How_the_Web_works\">How the web works</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/HTML\">HTML — Structuring the web</a></li><li><details><summary>Introduction to HTML</summary><ol><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML\">Introduction to HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started\">Getting started with HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML\">What's in the head? Metadata in HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/HTML_text_fundamentals\">HTML text fundamentals</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks\">Creating hyperlinks</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Advanced_text_formatting\">Advanced text formatting</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure\">Document and website structure</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Debugging_HTML\">Debugging HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Marking_up_a_letter\">Marking up a letter</a></li><li><a href=\"/en-US/docs/Learn/HTML/Introduction_to_HTML/Structuring_a_page_of_content\">Structuring a page of content</a></li></ol></details></li><li><details><summary>Multimedia and embedding</summary><ol><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding\">Multimedia and embedding</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML\">Images in HTML</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content\">Video and audio content</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Other_embedding_technologies\">From object to iframe — other embedding technologies</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web\">Adding vector graphics to the web</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images\">Responsive images</a></li><li><a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding/Mozilla_splash_page\">Mozilla splash page</a></li></ol></details></li><li><details><summary>HTML tables</summary><ol><li><a href=\"/en-US/docs/Learn/HTML/Tables\">HTML tables</a></li><li><a href=\"/en-US/docs/Learn/HTML/Tables/Basics\">HTML table basics</a></li><li><a href=\"/en-US/docs/Learn/HTML/Tables/Advanced\">HTML table advanced features and accessibility</a></li><li><a href=\"/en-US/docs/Learn/HTML/Tables/Structuring_planet_data\">Structuring planet data</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/CSS\">CSS — Styling the web</a></li><li><details><summary>CSS first steps</summary><ol><li><a href=\"/en-US/docs/Learn/CSS/First_steps\">CSS first steps</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/What_is_CSS\">What is CSS?</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/Getting_started\">Getting started with CSS</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/How_CSS_is_structured\">How CSS is structured</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/How_CSS_works\">How CSS works</a></li><li><a href=\"/en-US/docs/Learn/CSS/First_steps/Styling_a_biography_page\">Styling a biography page</a></li></ol></details></li><li><details><summary>CSS building blocks</summary><ol><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks\">CSS building blocks</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors\">CSS selectors</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors/Type_Class_and_ID_Selectors\">Type, class, and ID selectors</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors/Attribute_selectors\">Attribute selectors</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements\">Pseudo-classes and pseudo-elements</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Selectors/Combinators\">Combinators</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance\">Cascade, specificity, and inheritance</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers\">Cascade layers</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/The_box_model\">The box model</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Backgrounds_and_borders\">Backgrounds and borders</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Handling_different_text_directions\">Handling different text directions</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Overflowing_content\">Overflowing content</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Values_and_units\">CSS values and units</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Sizing_items_in_CSS\">Sizing items in CSS</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Images_media_form_elements\">Images, media, and form elements</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Styling_tables\">Styling tables</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Advanced_styling_effects\">Advanced styling effects</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Debugging_CSS\">Debugging CSS</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Organizing\">Organizing your CSS</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Fundamental_CSS_comprehension\">Fundamental CSS comprehension</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/Creating_fancy_letterheaded_paper\">Creating fancy letterheaded paper</a></li><li><a href=\"/en-US/docs/Learn/CSS/Building_blocks/A_cool_looking_box\">A cool-looking box</a></li></ol></details></li><li><details><summary>Styling text</summary><ol><li><a href=\"/en-US/docs/Learn/CSS/Styling_text\">CSS styling text</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Fundamentals\">Fundamental text and font styling</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Styling_lists\">Styling lists</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Styling_links\">Styling links</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Web_fonts\">Web fonts</a></li><li><a href=\"/en-US/docs/Learn/CSS/Styling_text/Typesetting_a_homepage\">Typesetting a community school homepage</a></li></ol></details></li><li><details><summary>CSS layout</summary><ol><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout\">CSS layout</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Introduction\">Introduction to CSS layout</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Normal_Flow\">Normal Flow</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Flexbox\">Flexbox</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Grids\">Grids</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Floats\">Floats</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Positioning\">Positioning</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Multiple-column_Layout\">Multiple-column layout</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design\">Responsive design</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Media_queries\">Beginner's guide to media queries</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods\">Legacy layout methods</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Supporting_Older_Browsers\">Supporting older browsers</a></li><li><a href=\"/en-US/docs/Learn/CSS/CSS_layout/Fundamental_Layout_Comprehension\">Fundamental layout comprehension</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/JavaScript\">JavaScript — Dynamic client-side scripting</a></li><li><details><summary>JavaScript first steps</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps\">JavaScript first steps</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript\">What is JavaScript?</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/A_first_splash\">A first splash into JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong\">What went wrong? Troubleshooting JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Variables\">Storing the information you need — Variables</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Math\">Basic math in JavaScript — numbers and operators</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Strings\">Handling text — strings in JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods\">Useful string methods</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Arrays\">Arrays</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator\">Silly story generator</a></li></ol></details></li><li><details><summary>JavaScript building blocks</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks\">JavaScript building blocks</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/conditionals\">Making decisions in your code — conditionals</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code\">Looping code</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Functions\">Functions — reusable blocks of code</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function\">Build your own function</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Return_values\">Function return values</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Events\">Introduction to events</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Event_bubbling\">Event bubbling</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Building_blocks/Image_gallery\">Image gallery</a></li></ol></details></li><li><details><summary>Introducing JavaScript objects</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/Objects\">Introducing JavaScript objects</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Basics\">JavaScript object basics</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Object_prototypes\">Object prototypes</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Object-oriented_programming\">Object-oriented programming</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript\">Classes in JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/JSON\">Working with JSON</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Object_building_practice\">Object building practice</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features\">Adding features to our bouncing balls demo</a></li></ol></details></li><li><details><summary>Asynchronous JavaScript</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous\">Asynchronous JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Introducing\">Introducing asynchronous JavaScript</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Promises\">How to use promises</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Implementing_a_promise-based_API\">How to implement a promise-based API</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Introducing_workers\">Introducing workers</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Asynchronous/Sequencing_animations\">Sequencing animations</a></li></ol></details></li><li><details open=\"\"><summary>Client-side web APIs</summary><ol><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs\">Client-side web APIs</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction\">Introduction to web APIs</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents\">Manipulating documents</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data\">Fetching data from the server</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs\">Third-party APIs</a></li><li><em><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics\" aria-current=\"page\">Drawing graphics</a></em></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs\">Video and Audio APIs</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage\">Client-side storage</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Forms\">Web forms — Working with user data</a></li><li><details><summary>Web form building blocks</summary><ol><li><a href=\"/en-US/docs/Learn/Forms\">Web form building blocks</a></li><li><a href=\"/en-US/docs/Learn/Forms/Your_first_form\">Your first form</a></li><li><a href=\"/en-US/docs/Learn/Forms/How_to_structure_a_web_form\">How to structure a web form</a></li><li><a href=\"/en-US/docs/Learn/Forms/Basic_native_form_controls\">Basic native form controls</a></li><li><a href=\"/en-US/docs/Learn/Forms/HTML5_input_types\">The HTML5 input types</a></li><li><a href=\"/en-US/docs/Learn/Forms/Other_form_controls\">Other form controls</a></li><li><a href=\"/en-US/docs/Learn/Forms/Styling_web_forms\">Styling web forms</a></li><li><a href=\"/en-US/docs/Learn/Forms/Advanced_form_styling\">Advanced form styling</a></li><li><a href=\"/en-US/docs/Learn/Forms/UI_pseudo-classes\">UI pseudo-classes</a></li><li><a href=\"/en-US/docs/Learn/Forms/Form_validation\">Client-side form validation</a></li><li><a href=\"/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data\">Sending form data</a></li></ol></details></li><li><details><summary>Advanced web form techniques</summary><ol><li><a href=\"/en-US/docs/Learn/Forms/How_to_build_custom_form_controls\">How to build custom form controls</a></li><li><a href=\"/en-US/docs/Learn/Forms/Sending_forms_through_JavaScript\">Sending forms through JavaScript</a></li><li><a href=\"/en-US/docs/Learn/Forms/Property_compatibility_table_for_form_controls\">CSS property compatibility table for form controls</a></li><li><a href=\"/en-US/docs/Learn/Forms/HTML_forms_in_legacy_browsers\">HTML forms in legacy browsers</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Accessibility\">Accessibility — Make the web usable by everyone</a></li><li><details><summary>Accessibility guides</summary><ol><li><a href=\"/en-US/docs/Learn/Accessibility\">Accessibility</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/What_is_accessibility\">What is accessibility?</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/HTML\">HTML: A good basis for accessibility</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/CSS_and_JavaScript\">CSS and JavaScript accessibility best practices</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/WAI-ARIA_basics\">WAI-ARIA basics</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/Multimedia\">Accessible multimedia</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/Mobile\">Mobile accessibility</a></li><li><a href=\"/en-US/docs/Learn/Accessibility/Accessibility_troubleshooting\">Assessment: Accessibility troubleshooting</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Performance\">Performance — Making websites fast and responsive</a></li><li><details><summary>Performance guides</summary><ol><li><a href=\"/en-US/docs/Learn/Performance\">Web performance</a></li><li><a href=\"/en-US/docs/Learn/Performance/why_web_performance\">The \"why\" of web performance</a></li><li><a href=\"/en-US/docs/Learn/Performance/What_is_web_performance\">What is web performance?</a></li><li><a href=\"/en-US/docs/Learn/Performance/Perceived_performance\">Perceived performance</a></li><li><a href=\"/en-US/docs/Learn/Performance/Measuring_performance\">Measuring performance</a></li><li><a href=\"/en-US/docs/Learn/Performance/Multimedia\">Multimedia: Images</a></li><li><a href=\"/en-US/docs/Learn/Performance/video\">Multimedia: video</a></li><li><a href=\"/en-US/docs/Learn/Performance/JavaScript\">JavaScript performance optimization</a></li><li><a href=\"/en-US/docs/Learn/Performance/HTML\">HTML performance optimization</a></li><li><a href=\"/en-US/docs/Learn/Performance/CSS\">CSS performance optimization</a></li><li><a href=\"/en-US/docs/Learn/Performance/business_case_for_performance\">The business case for web performance</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/MathML\">MathML — Writing mathematics with MathML</a></li><li><details><summary>MathML first steps</summary><ol><li><a href=\"/en-US/docs/Learn/MathML/First_steps\">MathML first steps</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Getting_started\">Getting started with MathML</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Text_containers\">MathML Text Containers</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Fractions_and_roots\">MathML fractions and roots</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Scripts\">MathML scripted elements</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Tables\">MathML tables</a></li><li><a href=\"/en-US/docs/Learn/MathML/First_steps/Three_famous_mathematical_formulas\">Three famous mathematical formulas</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/../Games\">Games — Developing games for the web</a></li><li><details><summary>Guides and tutorials</summary><ol><li><a href=\"/en-US/docs/Games/Introduction\">Introduction to game development for the Web</a></li><li><a href=\"/en-US/docs/Games/Techniques\">Techniques for game development</a></li><li><a href=\"/en-US/docs/Games/Tutorials\">Tutorials</a></li><li><a href=\"/en-US/docs/Games/Publishing_games\">Publishing games</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Tools_and_testing\">Tools and testing</a></li><li><details><summary>Client-side web development tools</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools\">Understanding client-side web development tools</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview\">Client-side tooling overview</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line\">Command line crash course</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management\">Package management basics</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Introducing_complete_toolchain\">Introducing a complete toolchain</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Deployment\">Deploying our app</a></li></ol></details></li><li><details><summary>Introduction to client-side frameworks</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction\">Introduction to client-side frameworks</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features\">Framework main features</a></li></ol></details></li><li><details><summary>React</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started\">Getting started with React</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning\">Beginning our React todo list</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components\">Componentizing our React app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state\">React interactivity: Events and state</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering\">React interactivity: Editing, filtering, conditional rendering</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility\">Accessibility in React</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources\">React resources</a></li></ol></details></li><li><details><summary>Ember</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started\">Getting started with Ember</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization\">Ember app structure and componentization</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state\">Ember interactivity: Events, classes and state</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer\">Ember Interactivity: Footer functionality, conditional rendering</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing\">Routing in Ember</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources\">Ember resources and troubleshooting</a></li></ol></details></li><li><details><summary>Vue</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started\">Getting started with Vue</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component\">Creating our first Vue component</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_rendering_lists\">Rendering a list of Vue components</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_methods_events_models\">Adding a new todo form: Vue events, methods, and models</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_styling\">Styling Vue components with CSS</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties\">Using Vue computed properties</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_conditional_rendering\">Vue conditional rendering: editing existing todos</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management\">Vue refs and lifecycle methods for focus management</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_resources\">Vue resources</a></li></ol></details></li><li><details><summary>Svelte</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started\">Getting started with Svelte</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning\">Starting our Svelte to-do list app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props\">Dynamic behavior in Svelte: working with variables and props</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components\">Componentizing our Svelte app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility\">Advanced Svelte: Reactivity, lifecycle, accessibility</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores\">Working with Svelte stores</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript\">TypeScript support in Svelte</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_deployment_next\">Deployment and next steps</a></li></ol></details></li><li><details><summary>Angular</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_getting_started\">Getting started with Angular</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning\">Beginning our Angular todo list app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_styling\">Styling our Angular app</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_item_component\">Creating an item component</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_filtering\">Filtering our to-do items</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_building\">Building Angular applications and further resources</a></li></ol></details></li><li><details><summary>Git and GitHub</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/GitHub\">Git and GitHub</a></li></ol></details></li><li><details><summary>Cross browser testing</summary><ol><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing\">Cross browser testing</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Introduction\">Introduction to cross-browser testing</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies\">Strategies for carrying out testing</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS\">Handling common HTML and CSS problems</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript\">Handling common JavaScript problems</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility\">Handling common accessibility problems</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection\">Implementing feature detection</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Automated_testing\">Introduction to automated testing</a></li><li><a href=\"/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment\">Setting up your own test automation environment</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Server-side\">Server-side website programming</a></li><li><details><summary>First steps</summary><ol><li><a href=\"/en-US/docs/Learn/Server-side/First_steps\">Server-side website programming first steps</a></li><li><a href=\"/en-US/docs/Learn/Server-side/First_steps/Introduction\">Introduction to the server side</a></li><li><a href=\"/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview\">Client-Server Overview</a></li><li><a href=\"/en-US/docs/Learn/Server-side/First_steps/Web_frameworks\">Server-side web frameworks</a></li><li><a href=\"/en-US/docs/Learn/Server-side/First_steps/Website_security\">Website security</a></li></ol></details></li><li><details><summary>Django web framework (Python)</summary><ol><li><a href=\"/en-US/docs/Learn/Server-side/Django\">Django Web Framework (Python)</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Introduction\">Django introduction</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/development_environment\">Setting up a Django development environment</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website\">Django Tutorial: The Local Library website</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/skeleton_website\">Django Tutorial Part 2: Creating a skeleton website</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Models\">Django Tutorial Part 3: Using models</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Admin_site\">Django Tutorial Part 4: Django admin site</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Home_page\">Django Tutorial Part 5: Creating our home page</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Generic_views\">Django Tutorial Part 6: Generic list and detail views</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Sessions\">Django Tutorial Part 7: Sessions framework</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Authentication\">Django Tutorial Part 8: User authentication and permissions</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Forms\">Django Tutorial Part 9: Working with forms</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Testing\">Django Tutorial Part 10: Testing a Django web application</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/Deployment\">Django Tutorial Part 11: Deploying Django to production</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/web_application_security\">Django web application security</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Django/django_assessment_blog\">Assessment: DIY Django mini blog</a></li></ol></details></li><li><details><summary>Express Web Framework (Node.js/JavaScript)</summary><ol><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs\">Express web framework (Node.js/JavaScript)</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction\">Express/Node introduction</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment\">Setting up a Node development environment</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website\">Express Tutorial: The Local Library website</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website\">Express Tutorial Part 2: Creating a skeleton website</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose\">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/routes\">Express Tutorial Part 4: Routes and controllers</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data\">Express Tutorial Part 5: Displaying library data</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/forms\">Express Tutorial Part 6: Working with forms</a></li><li><a href=\"/en-US/docs/Learn/Server-side/Express_Nodejs/deployment\">Express Tutorial Part 7: Deploying to production</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn/Common_questions\">Further resources</a></li><li><details><summary>Common questions</summary><ol><li><a href=\"/en-US/docs/Learn/Common_questions\">Common questions</a></li><li><a href=\"/en-US/docs/Learn/HTML/Howto\">Use HTML to solve common problems</a></li><li><a href=\"/en-US/docs/Learn/CSS/Howto\">Use CSS to solve common problems</a></li><li><a href=\"/en-US/docs/Learn/JavaScript/Howto\">Solve common problems in your JavaScript code</a></li><li><a href=\"/en-US/docs/Learn/Common_questions/Web_mechanics\">Web mechanics</a></li><li><a href=\"/en-US/docs/Learn/Common_questions/Tools_and_setup\">Tools and setup</a></li><li><a href=\"/en-US/docs/Learn/Common_questions/Design_and_accessibility\">Design and accessibility</a></li></ol></details></li></ol>","sidebarMacro":"LearnSidebar","body":[{"type":"prose","value":{"id":null,"title":null,"isH3":false,"content":"<ul class=\"prev-next\">\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs\"><span class=\"button-wrap\"> Previous </span></a></li>\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs\"><span class=\"button-wrap\"> Overview: Client-side web APIs</span></a></li>\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs\"><span class=\"button-wrap\"> Next </span></a></li>\n</ul>\n<p>The browser contains some very powerful graphics programming tools, from the Scalable Vector Graphics (<a href=\"/en-US/docs/Web/SVG\">SVG</a>) language, to APIs for drawing on HTML <a href=\"/en-US/docs/Web/HTML/Element/canvas\"><code><canvas></code></a> elements, (see <a href=\"/en-US/docs/Web/API/Canvas_API\">The Canvas API</a> and <a href=\"/en-US/docs/Web/API/WebGL_API\">WebGL</a>). This article provides an introduction to canvas, and further resources to allow you to learn more.</p>\n<figure class=\"table-container\"><table>\n <tbody>\n <tr>\n <th scope=\"row\">Prerequisites:</th>\n <td>\n JavaScript basics (see\n <a href=\"/en-US/docs/Learn/JavaScript/First_steps\">first steps</a>,\n <a href=\"/en-US/docs/Learn/JavaScript/Building_blocks\">building blocks</a>,\n <a href=\"/en-US/docs/Learn/JavaScript/Objects\">JavaScript objects</a>),\n the\n <a href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction\">basics of Client-side APIs</a>\n </td>\n </tr>\n <tr>\n <th scope=\"row\">Objective:</th>\n <td>\n To learn the basics of drawing on <code><canvas></code> elements\n using JavaScript.\n </td>\n </tr>\n </tbody>\n</table></figure>"}},{"type":"prose","value":{"id":"graphics_on_the_web","title":"Graphics on the Web","isH3":false,"content":"<p>As we talked about in our HTML <a href=\"/en-US/docs/Learn/HTML/Multimedia_and_embedding\">Multimedia and embedding</a> module, the Web was originally just text, which was very boring, so images were introduced — first via the <a href=\"/en-US/docs/Web/HTML/Element/img\"><code><img></code></a> element and later via CSS properties such as <a href=\"/en-US/docs/Web/CSS/background-image\"><code>background-image</code></a>, and <a href=\"/en-US/docs/Web/SVG\">SVG</a>.</p>\n<p>This however was still not enough. While you could use <a href=\"/en-US/docs/Learn/CSS\">CSS</a> and <a href=\"/en-US/docs/Learn/JavaScript\">JavaScript</a> to animate (and otherwise manipulate) SVG vector images — as they are represented by markup — there was still no way to do the same for bitmap images, and the tools available were rather limited. The Web still had no way to effectively create animations, games, 3D scenes, and other requirements commonly handled by lower level languages such as C++ or Java.</p>\n<p>The situation started to improve when browsers began to support the <a href=\"/en-US/docs/Web/HTML/Element/canvas\"><code><canvas></code></a> element and associated <a href=\"/en-US/docs/Web/API/Canvas_API\">Canvas API</a> in 2004. As you'll see below, canvas provides some useful tools for creating 2D animations, games, data visualizations, and other types of applications, especially when combined with some of the other APIs the web platform provides, but can be difficult or impossible to make accessible</p>\n<p>The below example shows a simple 2D canvas-based bouncing balls animation that we originally met in our <a href=\"/en-US/docs/Learn/JavaScript/Objects/Object_building_practice\">Introducing JavaScript objects</a> module:</p><iframe width=\"100%\" height=\"500\" src=\"https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html\" loading=\"lazy\"></iframe>\n<p>Around 2006–2007, Mozilla started work on an experimental 3D canvas implementation. This became <a href=\"/en-US/docs/Web/API/WebGL_API\">WebGL</a>, which gained traction among browser vendors, and was standardized around 2009–2010. WebGL allows you to create real 3D graphics inside your web browser; the below example shows a simple rotating WebGL cube:</p><iframe width=\"100%\" height=\"500\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html\" loading=\"lazy\"></iframe>\n<p>This article will focus mainly on 2D canvas, as raw WebGL code is very complex. We will however show how to use a WebGL library to create a 3D scene more easily, and you can find a tutorial covering raw WebGL elsewhere — see <a href=\"/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL\">Getting started with WebGL</a>.</p>"}},{"type":"prose","value":{"id":"active_learning_getting_started_with_a_canvas","title":"Active learning: Getting started with a <canvas>","isH3":false,"content":"<p>If you want to create a 2D <em>or</em> 3D scene on a web page, you need to start with an HTML <a href=\"/en-US/docs/Web/HTML/Element/canvas\"><code><canvas></code></a> element. This element is used to define the area on the page into which the image will be drawn. This is as simple as including the element on the page:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code><canvas width=\"320\" height=\"240\"></canvas>\n</code></pre></div>\n<p>This will create a canvas on the page with a size of 320 by 240 pixels.</p>\n<p>You should put some fallback content inside the <code><canvas></code> tags. This should describe the canvas content to users of browsers that don't support canvas, or users of screen readers.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code><canvas width=\"320\" height=\"240\">\n <p>Description of the canvas for those unable to view it.</p>\n</canvas>\n</code></pre></div>\n<p>The fallback should provide useful alternative content to the canvas content. For example, if you are rendering a constantly updating graph of stock prices, the fallback content could be a static image of the latest stock graph, with <code>alt</code> text saying what the prices are in text or a list of links to individual stock pages.</p>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> Canvas content is not accessible to screen readers. Include descriptive text as the value of the <a href=\"/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label\"><code>aria-label</code></a> attribute directly on the canvas element itself or include fallback content placed within the opening and closing <code><canvas></code> tags. Canvas content is not part of the DOM, but nested fallback content is.</p>\n</div>"}},{"type":"prose","value":{"id":"creating_and_sizing_our_canvas","title":"Creating and sizing our canvas","isH3":true,"content":"<p>Let's start by creating our own canvas that we draw future experiments on to.</p>\n<ol>\n <li>\n <p>First make a local copy of the <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/0_canvas_start\" class=\"external\" target=\"_blank\">0_canvas_start</a> directory. It contains three files:</p>\n <ul>\n <li>\"index.html\"</li>\n <li>\"script.js\"</li>\n <li>\"style.css\"</li>\n </ul>\n </li>\n <li>\n <p>Open \"index.html\", and add the following code into it, just below the opening <a href=\"/en-US/docs/Web/HTML/Element/body\"><code><body></code></a> tag:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code><canvas class=\"myCanvas\">\n <p>Add suitable fallback here.</p>\n</canvas>\n</code></pre></div>\n <p>We have added a <code>class</code> to the <code><canvas></code> element so it will be easier to select if we have multiple canvases on the page, but we have removed the <code>width</code> and <code>height</code> attributes for now (you could add them back in if you wanted, but we will set them using JavaScript in a below section). Canvases with no explicit width and height default to 300 pixels wide by 150 pixels high.</p>\n </li>\n <li>\n <p>Now open \"script.js\" and add the following lines of JavaScript:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const canvas = document.querySelector(\".myCanvas\");\nconst width = (canvas.width = window.innerWidth);\nconst height = (canvas.height = window.innerHeight);\n</code></pre></div>\n <p>Here we have stored a reference to the canvas in the <code>canvas</code> constant. In the second line we set both a new constant <code>width</code> and the canvas' <code>width</code> property equal to <a href=\"/en-US/docs/Web/API/Window/innerWidth\"><code>Window.innerWidth</code></a> (which gives us the viewport width). In the third line we set both a new constant <code>height</code> and the canvas' <code>height</code> property equal to <a href=\"/en-US/docs/Web/API/Window/innerHeight\"><code>Window.innerHeight</code></a> (which gives us the viewport height). So now we have a canvas that fills the entire width and height of the browser window!</p>\n <p>You'll also see that we are chaining assignments together with multiple equals signs — this is allowed in JavaScript, and it is a good technique if you want to make multiple variables all equal to the same value. We wanted to make the canvas width and height easily accessible in the width/height variables, as they are useful values to have available for later (for example, if you want to draw something exactly halfway across the width of the canvas).</p>\n </li>\n</ol>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> You should generally set the size of the image using HTML attributes or DOM properties, as explained above. You could use CSS, but the trouble then is that the sizing is done after the canvas has rendered, and just like any other image (the rendered canvas is just an image), the image could become pixelated/distorted.</p>\n</div>"}},{"type":"prose","value":{"id":"getting_the_canvas_context_and_final_setup","title":"Getting the canvas context and final setup","isH3":true,"content":"<p>We need to do one final thing before we can consider our canvas template finished. To draw onto the canvas we need to get a special reference to the drawing area called a context. This is done using the <a href=\"/en-US/docs/Web/API/HTMLCanvasElement/getContext\"><code>HTMLCanvasElement.getContext()</code></a> method, which for basic usage takes a single string as a parameter representing the type of context you want to retrieve.</p>\n<p>In this case we want a 2d canvas, so add the following JavaScript line below the others in \"script.js\":</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const ctx = canvas.getContext(\"2d\");\n</code></pre></div>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> Other context values you could choose include <code>webgl</code> for WebGL, <code>webgl2</code> for WebGL 2, etc., but we won't need those in this article.</p>\n</div>\n<p>So that's it — our canvas is now primed and ready for drawing on! The <code>ctx</code> variable now contains a <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D\"><code>CanvasRenderingContext2D</code></a> object, and all drawing operations on the canvas will involve manipulating this object.</p>\n<p>Let's do one last thing before we move on. We'll color the canvas background black to give you a first taste of the canvas API. Add the following lines at the bottom of your JavaScript:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(0 0 0)\";\nctx.fillRect(0, 0, width, height);\n</code></pre></div>\n<p>Here we are setting a fill color using the canvas' <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle\" title=\"fillStyle\"><code>fillStyle</code></a> property (this takes <a href=\"/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#color\">color values</a> just like CSS properties do), then drawing a rectangle that covers the entire area of the canvas with the <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/fillRect\" title=\"fillRect\"><code>fillRect</code></a> method (the first two parameters are the coordinates of the rectangle's top left-hand corner; the last two are the width and height you want the rectangle drawn at — we told you those <code>width</code> and <code>height</code> variables would be useful)!</p>\n<p>OK, our template is done and it's time to move on.</p>"}},{"type":"prose","value":{"id":"2d_canvas_basics","title":"2D canvas basics","isH3":false,"content":"<p>As we said above, all drawing operations are done by manipulating a <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D\"><code>CanvasRenderingContext2D</code></a> object (in our case, <code>ctx</code>). Many operations need to be given coordinates to pinpoint exactly where to draw something — the top left of the canvas is point (0, 0), the horizontal (x) axis runs from left to right, and the vertical (y) axis runs from top to bottom.</p>\n<p>\n <img src=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics/canvas_default_grid.png\" alt=\"Gridded graph paper with small squares covering its area with a steelblue square in the middle. The top left corner of the canvas is point (0, 0) of the canvas x-axis and y-axis. The horizontal (x) axis runs from left to right denoting the width, and the vertical (y) axis runs from top to bottom denotes the height. The top left corner of the blue square is labeled as being a distance of x units from the y-axis and y units from the x-axis.\" width=\"220\" height=\"220\" loading=\"lazy\">\n</p>\n<p>Drawing shapes tends to be done using the rectangle shape primitive, or by tracing a line along a certain path and then filling in the shape. Below we'll show how to do both.</p>"}},{"type":"prose","value":{"id":"simple_rectangles","title":"Simple rectangles","isH3":true,"content":"<p>Let's start with some simple rectangles.</p>\n<ol>\n <li>\n <p>First of all, take a copy of your newly coded canvas template (or make a local copy of the <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a> directory if you didn't follow the above steps).</p>\n </li>\n <li>\n <p>Next, add the following lines to the bottom of your JavaScript:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(255 0 0)\";\nctx.fillRect(50, 50, 100, 150);\n</code></pre></div>\n <p>If you save and refresh, you should see a red rectangle has appeared on your canvas. Its top left corner is 50 pixels away from the top and left of the canvas edge (as defined by the first two parameters), and it is 100 pixels wide and 150 pixels tall (as defined by the third and fourth parameters).</p>\n </li>\n <li>\n <p>Let's add another rectangle into the mix — a green one this time. Add the following at the bottom of your JavaScript:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(0 255 0)\";\nctx.fillRect(75, 75, 100, 100);\n</code></pre></div>\n <p>Save and refresh, and you'll see your new rectangle. This raises an important point: graphics operations like drawing rectangles, lines, and so forth are performed in the order in which they occur. Think of it like painting a wall, where each coat of paint overlaps and may even hide what's underneath. You can't do anything to change this, so you have to think carefully about the order in which you draw the graphics.</p>\n </li>\n <li>\n <p>Note that you can draw semi-transparent graphics by specifying a semi-transparent color, for example by using <code>rgb()</code>. The \"alpha channel\" defines the amount of transparency the color has. The higher its value, the more it will obscure whatever's behind it. Add the following to your code:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(255 0 255 / 75%)\";\nctx.fillRect(25, 100, 175, 50);\n</code></pre></div>\n </li>\n <li>\n <p>Now try drawing some more rectangles of your own; have fun!</p>\n </li>\n</ol>"}},{"type":"prose","value":{"id":"strokes_and_line_widths","title":"Strokes and line widths","isH3":true,"content":"<p>So far we've looked at drawing filled rectangles, but you can also draw rectangles that are just outlines (called <strong>strokes</strong> in graphic design). To set the color you want for your stroke, you use the <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/strokeStyle\" title=\"strokeStyle\"><code>strokeStyle</code></a> property; drawing a stroke rectangle is done using <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/strokeRect\" title=\"strokeRect\"><code>strokeRect</code></a>.</p>\n<ol>\n <li>\n <p>Add the following to the previous example, again below the previous JavaScript lines:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.strokeStyle = \"rgb(255 255 255)\";\nctx.strokeRect(25, 25, 175, 200);\n</code></pre></div>\n </li>\n <li>\n <p>The default width of strokes is 1 pixel; you can adjust the <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/lineWidth\" title=\"lineWidth\"><code>lineWidth</code></a> property value to change this (it takes a number representing the number of pixels wide the stroke is). Add the following line in between the previous two lines:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.lineWidth = 5;\n</code></pre></div>\n </li>\n</ol>\n<p>Now you should see that your white outline has become much thicker! That's it for now. At this point your example should look like this:</p><iframe width=\"100%\" height=\"250\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> The finished code is available on GitHub as <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles\" class=\"external\" target=\"_blank\">2_canvas_rectangles</a>.</p>\n</div>"}},{"type":"prose","value":{"id":"drawing_paths","title":"Drawing paths","isH3":true,"content":"<p>If you want to draw anything more complex than a rectangle, you need to draw a path. Basically, this involves writing code to specify exactly what path the pen should move along on your canvas to trace the shape you want to draw. Canvas includes functions for drawing straight lines, circles, Bézier curves, and more.</p>\n<p>Let's start the section off by making a fresh copy of our canvas template (<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>), in which to draw the new example.</p>\n<p>We'll be using some common methods and properties across all of the below sections:</p>\n<ul>\n <li><a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath\" title=\"beginPath()\"><code>beginPath()</code></a> — start drawing a path at the point where the pen currently is on the canvas. On a new canvas, the pen starts out at (0, 0).</li>\n <li><a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/moveTo\" title=\"moveTo()\"><code>moveTo()</code></a> — move the pen to a different point on the canvas, without recording or tracing the line; the pen \"jumps\" to the new position.</li>\n <li><a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/fill\" title=\"fill()\"><code>fill()</code></a> — draw a filled shape by filling in the path you've traced so far.</li>\n <li><a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/stroke\" title=\"stroke()\"><code>stroke()</code></a> — draw an outline shape by drawing a stroke along the path you've drawn so far.</li>\n <li>You can also use features like <code>lineWidth</code> and <code>fillStyle</code>/<code>strokeStyle</code> with paths as well as rectangles.</li>\n</ul>\n<p>A typical, simple path-drawing operation would look something like so:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(255 0 0)\";\nctx.beginPath();\nctx.moveTo(50, 50);\n// draw your path\nctx.fill();\n</code></pre></div>\n<h4 id=\"drawing_lines\">Drawing lines</h4>\n<p>Let's draw an equilateral triangle on the canvas.</p>\n<ol>\n <li>\n <p>First of all, add the following helper function to the bottom of your code. This converts degree values to radians, which is useful because whenever you need to provide an angle value in JavaScript, it will nearly always be in radians, but humans usually think in degrees.</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function degToRad(degrees) {\n return (degrees * Math.PI) / 180;\n}\n</code></pre></div>\n </li>\n <li>\n <p>Next, start off your path by adding the following below your previous addition; here we set a color for our triangle, start drawing a path, and then move the pen to (50, 50) without drawing anything. That's where we'll start drawing our triangle.</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(255 0 0)\";\nctx.beginPath();\nctx.moveTo(50, 50);\n</code></pre></div>\n </li>\n <li>\n <p>Now add the following lines at the bottom of your script:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.lineTo(150, 50);\nconst triHeight = 50 * Math.tan(degToRad(60));\nctx.lineTo(100, 50 + triHeight);\nctx.lineTo(50, 50);\nctx.fill();\n</code></pre></div>\n <p>Let's run through this in order:</p>\n <p>First we draw a line across to (150, 50) — our path now goes 100 pixels to the right along the x axis.</p>\n <p>Second, we work out the height of our equilateral triangle, using a bit of simple trigonometry. Basically, we are drawing the triangle pointing downwards. The angles in an equilateral triangle are always 60 degrees; to work out the height we can split it down the middle into two right-angled triangles, which will each have angles of 90 degrees, 60 degrees, and 30 degrees. In terms of the sides:</p>\n <ul>\n <li>The longest side is called the <strong>hypotenuse</strong></li>\n <li>The side next to the 60 degree angle is called the <strong>adjacent</strong> — which we know is 50 pixels, as it is half of the line we just drew.</li>\n <li>The side opposite the 60 degree angle is called the <strong>opposite</strong>, which is the height of the triangle we want to calculate.</li>\n </ul>\n <p>\n <img src=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics/trigonometry.png\" alt=\"An equilateral triangle pointing downwards with labeled angles and sides. The horizontal line at the top is labeled 'adjacent'. A perpendicular dotted line, from the middle of the adjacent line, labeled 'opposite', splits the triangle creating two equal right triangles. The right side of the triangle is labeled the hypotenuse, as it is the hypotenuse of the right triangle formed by the line labeled 'opposite'. while all three-sided of the triangle are of equal length, the hypotenuse is the longest side of the right triangle.\" width=\"200\" height=\"166\" loading=\"lazy\">\n </p>\n <p>One of the basic trigonometric formulae states that the length of the adjacent multiplied by the tangent of the angle is equal to the opposite, hence we come up with <code>50 * Math.tan(degToRad(60))</code>. We use our <code>degToRad()</code> function to convert 60 degrees to radians, as <a href=\"/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan\"><code>Math.tan()</code></a> expects an input value in radians.</p>\n </li>\n <li>\n <p>With the height calculated, we draw another line to <code>(100, 50 + triHeight)</code>. The X coordinate is simple; it must be halfway between the previous two X values we set. The Y value on the other hand must be 50 plus the triangle height, as we know the top of the triangle is 50 pixels from the top of the canvas.</p>\n </li>\n <li>\n <p>The next line draws a line back to the starting point of the triangle.</p>\n </li>\n <li>\n <p>Last of all, we run <code>ctx.fill()</code> to end the path and fill in the shape.</p>\n </li>\n</ol>\n<h4 id=\"drawing_circles\">Drawing circles</h4>\n<p>Now let's look at how to draw a circle in canvas. This is accomplished using the <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/arc\" title=\"arc()\"><code>arc()</code></a> method, which draws all or part of a circle at a specified point.</p>\n<ol>\n <li>\n <p>Let's add an arc to our canvas — add the following to the bottom of your code:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(0 0 255)\";\nctx.beginPath();\nctx.arc(150, 106, 50, degToRad(0), degToRad(360), false);\nctx.fill();\n</code></pre></div>\n <p><code>arc()</code> takes six parameters. The first two specify the position of the arc's center (X and Y, respectively). The third is the circle's radius, the fourth and fifth are the start and end angles at which to draw the circle (so specifying 0 and 360 degrees gives us a full circle), and the sixth parameter defines whether the circle should be drawn counterclockwise (anticlockwise) or clockwise (<code>false</code> is clockwise).</p>\n <div class=\"notecard note\">\n <p><strong>Note:</strong> 0 degrees is horizontally to the right.</p>\n </div>\n </li>\n <li>\n <p>Let's try adding another arc:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"yellow\";\nctx.beginPath();\nctx.arc(200, 106, 50, degToRad(-45), degToRad(45), true);\nctx.lineTo(200, 106);\nctx.fill();\n</code></pre></div>\n <p>The pattern here is very similar, but with two differences:</p>\n <ul>\n <li>We have set the last parameter of <code>arc()</code> to <code>true</code>, meaning that the arc is drawn counterclockwise, which means that even though the arc is specified as starting at -45 degrees and ending at 45 degrees, we draw the arc around the 270 degrees not inside this portion. If you were to change <code>true</code> to <code>false</code> and then re-run the code, only the 90 degree slice of the circle would be drawn.</li>\n <li>Before calling <code>fill()</code>, we draw a line to the center of the circle. This means that we get the rather nice Pac-Man-style cutout rendered. If you removed this line (try it!) then re-ran the code, you'd get just an edge of the circle chopped off between the start and end point of the arc. This illustrates another important point of the canvas — if you try to fill an incomplete path (i.e. one that is not closed), the browser fills in a straight line between the start and end point and then fills it in.</li>\n </ul>\n </li>\n</ol>\n<p>That's it for now; your final example should look like this:</p><iframe width=\"100%\" height=\"200\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/3_canvas_paths/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> The finished code is available on GitHub as <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/3_canvas_paths\" class=\"external\" target=\"_blank\">3_canvas_paths</a>.</p>\n</div>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> To find out more about advanced path drawing features such as Bézier curves, check out our <a href=\"/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes\">Drawing shapes with canvas</a> tutorial.</p>\n</div>"}},{"type":"prose","value":{"id":"text","title":"Text","isH3":true,"content":"<p>Canvas also has features for drawing text. Let's explore these briefly. Start by making another fresh copy of our canvas template (<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>) in which to draw the new example.</p>\n<p>Text is drawn using two methods:</p>\n<ul>\n <li><a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/fillText\" title=\"fillText()\"><code>fillText()</code></a> — draws filled text.</li>\n <li><a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/strokeText\" title=\"strokeText()\"><code>strokeText()</code></a> — draws outline (stroke) text.</li>\n</ul>\n<p>Both of these take three properties in their basic usage: the text string to draw and the X and Y coordinates of the point to start drawing the text at. This works out as the <strong>bottom left</strong> corner of the <strong>text box</strong> (literally, the box surrounding the text you draw), which might confuse you as other drawing operations tend to start from the top left corner — bear this in mind.</p>\n<p>There are also a number of properties to help control text rendering such as <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/font\" title=\"font\"><code>font</code></a>, which lets you specify font family, size, etc. It takes as its value the same syntax as the CSS <a href=\"/en-US/docs/Web/CSS/font\"><code>font</code></a> property.</p>\n<p>Canvas content is not accessible to screen readers. Text painted to the canvas is not available to the DOM, but must be made available to be accessible. In this example, we include the text as the value for <code>aria-label</code>.</p>\n<p>Try adding the following block to the bottom of your JavaScript:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.strokeStyle = \"white\";\nctx.lineWidth = 1;\nctx.font = \"36px arial\";\nctx.strokeText(\"Canvas text\", 50, 50);\n\nctx.fillStyle = \"red\";\nctx.font = \"48px georgia\";\nctx.fillText(\"Canvas text\", 50, 150);\n\ncanvas.setAttribute(\"aria-label\", \"Canvas text\");\n</code></pre></div>\n<p>Here we draw two lines of text, one outline and the other stroke. The final example should look like so:</p><iframe width=\"100%\" height=\"180\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/4_canvas_text/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> The finished code is available on GitHub as <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/4_canvas_text\" class=\"external\" target=\"_blank\">4_canvas_text</a>.</p>\n</div>\n<p>Have a play and see what you can come up with! You can find more information on the options available for canvas text at <a href=\"/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_text\">Drawing text</a>.</p>"}},{"type":"prose","value":{"id":"drawing_images_onto_canvas","title":"Drawing images onto canvas","isH3":true,"content":"<p>It is possible to render external images onto your canvas. These can be simple images, frames from videos, or the content of other canvases. For the moment we'll just look at the case of using some simple images on our canvas.</p>\n<ol>\n <li>\n <p>As before, make another fresh copy of our canvas template (<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>) in which to draw the new example.</p>\n <p>Images are drawn onto canvas using the <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage\" title=\"drawImage()\"><code>drawImage()</code></a> method. The simplest version takes three parameters — a reference to the image you want to render, and the X and Y coordinates of the image's top left corner.</p>\n </li>\n <li>\n <p>Let's start by getting an image source to embed in our canvas. Add the following lines to the bottom of your JavaScript:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const image = new Image();\nimage.src = \"firefox.png\";\n</code></pre></div>\n <p>Here we create a new <a href=\"/en-US/docs/Web/API/HTMLImageElement\"><code>HTMLImageElement</code></a> object using the <a href=\"/en-US/docs/Web/API/HTMLImageElement/Image\" title=\"Image()\"><code>Image()</code></a> constructor. The returned object is the same type as that which is returned when you grab a reference to an existing <a href=\"/en-US/docs/Web/HTML/Element/img\"><code><img></code></a> element. We then set its <a href=\"/en-US/docs/Web/HTML/Element/img#src\"><code>src</code></a> attribute to equal our Firefox logo image. At this point, the browser starts loading the image.</p>\n </li>\n <li>\n <p>We could now try to embed the image using <code>drawImage()</code>, but we need to make sure the image file has been loaded first, otherwise the code will fail. We can achieve this using the <code>load</code> event, which will only be fired when the image has finished loading. Add the following block below the previous one:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>image.addEventListener(\"load\", () => ctx.drawImage(image, 20, 20));\n</code></pre></div>\n <p>If you load your example in the browser now, you should see the image embedded in the canvas.</p>\n </li>\n <li>\n <p>But there's more! What if we want to display only a part of the image, or to resize it? We can do both with the more complex version of <code>drawImage()</code>. Update your <code>ctx.drawImage()</code> line like so:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.drawImage(image, 20, 20, 185, 175, 50, 50, 185, 175);\n</code></pre></div>\n <ul>\n <li>The first parameter is the image reference, as before.</li>\n <li>Parameters 2 and 3 define the coordinates of the top left corner of the area you want to cut out of the loaded image, relative to the top-left corner of the image itself. Nothing to the left of the first parameter or above the second will be drawn.</li>\n <li>Parameters 4 and 5 define the width and height of the area we want to cut out from the original image we loaded.</li>\n <li>Parameters 6 and 7 define the coordinates at which you want to draw the top-left corner of the cut-out portion of the image, relative to the top-left corner of the canvas.</li>\n <li>Parameters 8 and 9 define the width and height to draw the cut-out area of the image. In this case, we have specified the same dimensions as the original slice, but you could resize it by specifying different values.</li>\n </ul>\n </li>\n <li>\n <p>When the image is meaningfully updated, the <a href=\"/en-US/docs/Glossary/Accessible_description\">accessible description</a> must also be updated.</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>canvas.setAttribute(\"aria-label\", \"Firefox Logo\");\n</code></pre></div>\n </li>\n</ol>\n<p>The final example should look like so:</p><iframe width=\"100%\" height=\"260\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/5_canvas_images/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> The finished code is available on GitHub as <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/5_canvas_images\" class=\"external\" target=\"_blank\">5_canvas_images</a>.</p>\n</div>"}},{"type":"prose","value":{"id":"loops_and_animations","title":"Loops and animations","isH3":false,"content":"<p>We have so far covered some very basic uses of 2D canvas, but really you won't experience the full power of canvas unless you update or animate it in some way. After all, canvas does provide scriptable images! If you aren't going to change anything, then you might as well just use static images and save yourself all the work.</p>"}},{"type":"prose","value":{"id":"creating_a_loop","title":"Creating a loop","isH3":true,"content":"<p>Playing with loops in canvas is rather fun — you can run canvas commands inside a <a href=\"/en-US/docs/Web/JavaScript/Reference/Statements/for\"><code>for</code></a> (or other type of) loop just like any other JavaScript code.</p>\n<p>Let's build an example.</p>\n<ol>\n <li>\n <p>Make another fresh copy of our canvas template (<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>) and open it in your code editor.</p>\n </li>\n <li>\n <p>Add the following line to the bottom of your JavaScript. This contains a new method, <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/translate\" title=\"translate()\"><code>translate()</code></a>, which moves the origin point of the canvas:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.translate(width / 2, height / 2);\n</code></pre></div>\n <p>This causes the coordinate origin (0, 0) to be moved to the center of the canvas, rather than being at the top left corner. This is very useful in many situations, like this one, where we want our design to be drawn relative to the center of the canvas.</p>\n </li>\n <li>\n <p>Now add the following code to the bottom of the JavaScript:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function degToRad(degrees) {\n return (degrees * Math.PI) / 180;\n}\n\nfunction rand(min, max) {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nlet length = 250;\nlet moveOffset = 20;\n\nfor (let i = 0; i < length; i++) {}\n</code></pre></div>\n <p>Here we are implementing the same <code>degToRad()</code> function we saw in the triangle example above, a <code>rand()</code> function that returns a random number between given lower and upper bounds, <code>length</code> and <code>moveOffset</code> variables (which we'll find out more about later), and an empty <code>for</code> loop.</p>\n </li>\n <li>\n <p>The idea here is that we'll draw something on the canvas inside the <code>for</code> loop, and iterate on it each time so we can create something interesting. Add the following code inside your <code>for</code> loop:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = `rgb(${255 - length} 0 ${255 - length} / 90%)`;\nctx.beginPath();\nctx.moveTo(moveOffset, moveOffset);\nctx.lineTo(moveOffset + length, moveOffset);\nconst triHeight = (length / 2) * Math.tan(degToRad(60));\nctx.lineTo(moveOffset + length / 2, moveOffset + triHeight);\nctx.lineTo(moveOffset, moveOffset);\nctx.fill();\n\nlength--;\nmoveOffset += 0.7;\nctx.rotate(degToRad(5));\n</code></pre></div>\n <p>So on each iteration, we:</p>\n <ul>\n <li>Set the <code>fillStyle</code> to be a shade of slightly transparent purple, which changes each time based on the value of <code>length</code>. As you'll see later the length gets smaller each time the loop runs, so the effect here is that the color gets brighter with each successive triangle drawn.</li>\n <li>Begin the path.</li>\n <li>Move the pen to a coordinate of <code>(moveOffset, moveOffset)</code>; This variable defines how far we want to move each time we draw a new triangle.</li>\n <li>Draw a line to a coordinate of <code>(moveOffset+length, moveOffset)</code>. This draws a line of length <code>length</code> parallel to the X axis.</li>\n <li>Calculate the triangle's height, as before.</li>\n <li>Draw a line to the downward-pointing corner of the triangle, then draw a line back to the start of the triangle.</li>\n <li>Call <code>fill()</code> to fill in the triangle.</li>\n <li>Update the variables that describe the sequence of triangles, so we can be ready to draw the next one. We decrease the <code>length</code> value by 1, so the triangles get smaller each time; increase <code>moveOffset</code> by a small amount so each successive triangle is slightly further away, and use another new function, <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/rotate\" title=\"rotate()\"><code>rotate()</code></a>, which allows us to rotate the entire canvas! We rotate it by 5 degrees before drawing the next triangle.</li>\n </ul>\n </li>\n</ol>\n<p>That's it! The final example should look like so:</p><iframe width=\"100%\" height=\"550\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop/index.html\" loading=\"lazy\"></iframe>\n<p>At this point, we'd like to encourage you to play with the example and make it your own! For example:</p>\n<ul>\n <li>Draw rectangles or arcs instead of triangles, or even embed images.</li>\n <li>Play with the <code>length</code> and <code>moveOffset</code> values.</li>\n <li>Introduce some random numbers using that <code>rand()</code> function we included above but didn't use.</li>\n</ul>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> The finished code is available on GitHub as <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop\" class=\"external\" target=\"_blank\">6_canvas_for_loop</a>.</p>\n</div>"}},{"type":"prose","value":{"id":"animations","title":"Animations","isH3":true,"content":"<p>The loop example we built above was fun, but really you need a constant loop that keeps going and going for any serious canvas applications (such as games and real time visualizations). If you think of your canvas as being like a movie, you really want the display to update on each frame to show the updated view, with an ideal refresh rate of 60 frames per second so that movement appears nice and smooth to the human eye.</p>\n<p>There are a few JavaScript functions that will allow you to run functions repeatedly, several times a second, the best one for our purposes here being <a href=\"/en-US/docs/Web/API/Window/requestAnimationFrame\"><code>window.requestAnimationFrame()</code></a>. It takes one parameter — the name of the function you want to run for each frame. The next time the browser is ready to update the screen, your function will get called. If that function draws the new update to your animation, then calls <code>requestAnimationFrame()</code> again just before the end of the function, the animation loop will continue to run. The loop ends when you stop calling <code>requestAnimationFrame()</code> or if you call <a href=\"/en-US/docs/Web/API/Window/cancelAnimationFrame\"><code>window.cancelAnimationFrame()</code></a> after calling <code>requestAnimationFrame()</code> but before the frame is called.</p>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> It's good practice to call <code>cancelAnimationFrame()</code> from your main code when you're done using the animation, to ensure that no updates are still waiting to be run.</p>\n</div>\n<p>The browser works out complex details such as making the animation run at a consistent speed, and not wasting resources animating things that can't be seen.</p>\n<p>To see how it works, let's quickly look again at our Bouncing Balls example (<a href=\"https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html\" class=\"external\" target=\"_blank\">see it live</a>, and also see <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/oojs/bouncing-balls\" class=\"external\" target=\"_blank\">the source code</a>). The code for the loop that keeps everything moving looks like this:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function loop() {\n ctx.fillStyle = \"rgb(0 0 0 / 25%)\";\n ctx.fillRect(0, 0, width, height);\n\n for (const ball of balls) {\n ball.draw();\n ball.update();\n ball.collisionDetect();\n }\n\n requestAnimationFrame(loop);\n}\n\nloop();\n</code></pre></div>\n<p>We run the <code>loop()</code> function once at the bottom of the code to start the cycle, drawing the first animation frame; the <code>loop()</code> function then takes charge of calling <code>requestAnimationFrame(loop)</code> to run the next frame of the animation, again and again.</p>\n<p>Note that on each frame we are completely clearing the canvas and redrawing everything. For every ball present we draw it, update its position, and check to see if it is colliding with any other balls. Once you've drawn a graphic to a canvas, there's no way to manipulate that graphic individually like you can with DOM elements. You can't move each ball around on the canvas, because once it's drawn, it's part of the canvas, and is not an individual accessible element or object. Instead, you have to erase and redraw, either by erasing the entire frame and redrawing everything, or by having code that knows exactly what parts need to be erased and only erases and redraws the minimum area of the canvas necessary.</p>\n<p>Optimizing animation of graphics is an entire specialty of programming, with lots of clever techniques available. Those are beyond what we need for our example, though!</p>\n<p>In general, the process of doing a canvas animation involves the following steps:</p>\n<ol>\n <li>Clear the canvas contents (e.g. with <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/fillRect\" title=\"fillRect()\"><code>fillRect()</code></a> or <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/clearRect\" title=\"clearRect()\"><code>clearRect()</code></a>).</li>\n <li>Save state (if necessary) using <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/save\" title=\"save()\"><code>save()</code></a> — this is needed when you want to save settings you've updated on the canvas before continuing, which is useful for more advanced applications.</li>\n <li>Draw the graphics you are animating.</li>\n <li>Restore the settings you saved in step 2, using <a href=\"/en-US/docs/Web/API/CanvasRenderingContext2D/restore\" title=\"restore()\"><code>restore()</code></a></li>\n <li>Call <code>requestAnimationFrame()</code> to schedule drawing of the next frame of the animation.</li>\n</ol>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> We won't cover <code>save()</code> and <code>restore()</code> here, but they are explained nicely in our <a href=\"/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations\">Transformations</a> tutorial (and the ones that follow it).</p>\n</div>"}},{"type":"prose","value":{"id":"a_simple_character_animation","title":"A simple character animation","isH3":true,"content":"<p>Now let's create our own simple animation — we'll get a character from a certain rather awesome retro computer game to walk across the screen.</p>\n<ol>\n <li>\n <p>Make another fresh copy of our canvas template (<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>) and open it in your code editor.</p>\n </li>\n <li>\n <p>Update the inner HTML to reflect the image:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code><canvas class=\"myCanvas\">\n <p>A man walking.</p>\n</canvas>\n</code></pre></div>\n </li>\n <li>\n <p>At the bottom of the JavaScript, add the following line to once again make the coordinate origin sit in the middle of the canvas:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.translate(width / 2, height / 2);\n</code></pre></div>\n </li>\n <li>\n <p>Now let's create a new <a href=\"/en-US/docs/Web/API/HTMLImageElement\"><code>HTMLImageElement</code></a> object, set its <a href=\"/en-US/docs/Web/HTML/Element/img#src\"><code>src</code></a> to the image we want to load, and add an <code>onload</code> event handler that will cause the <code>draw()</code> function to fire when the image is loaded:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const image = new Image();\nimage.src = \"walk-right.png\";\nimage.onload = draw;\n</code></pre></div>\n </li>\n <li>\n <p>Now we'll add some variables to keep track of the position the sprite is to be drawn on the screen, and the sprite number we want to display.</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let sprite = 0;\nlet posX = 0;\n</code></pre></div>\n <p>Let's explain the spritesheet image (which we have respectfully borrowed from Mike Thomas' <a href=\"https://codepen.io/mikethomas/pen/kQjKLW\" class=\"external\" target=\"_blank\">Walking cycle using CSS animation</a> CodePen). The image looks like this:</p>\n <p>\n <img src=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics/walk-right.png\" alt=\"A sprite sheet with six sprite images of a pixelated character resembling a walking person from their right side at different instances of a single step forward. The character has a white shirt with sky blue buttons, black trousers, and black shoes. Each sprite is 102 pixels wide and 148 pixels high.\" width=\"612\" height=\"148\" loading=\"lazy\">\n </p>\n <p>It contains six sprites that make up the whole walking sequence — each one is 102 pixels wide and 148 pixels high. To display each sprite cleanly we will have to use <code>drawImage()</code> to chop out a single sprite image from the spritesheet and display only that part, like we did above with the Firefox logo. The X coordinate of the slice will have to be a multiple of 102, and the Y coordinate will always be 0. The slice size will always be 102 by 148 pixels.</p>\n </li>\n <li>\n <p>Now let's insert an empty <code>draw()</code> function at the bottom of the code, ready for filling up with some code:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function draw() {}\n</code></pre></div>\n </li>\n <li>\n <p>The rest of the code in this section goes inside <code>draw()</code>. First, add the following line, which clears the canvas to prepare for drawing each frame. Notice that we have to specify the top-left corner of the rectangle as <code>-(width/2), -(height/2)</code> because we specified the origin position as <code>width/2, height/2</code> earlier on.</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillRect(-(width / 2), -(height / 2), width, height);\n</code></pre></div>\n </li>\n <li>\n <p>Next, we'll draw our image using drawImage — the 9-parameter version. Add the following:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.drawImage(image, sprite * 102, 0, 102, 148, 0 + posX, -74, 102, 148);\n</code></pre></div>\n <p>As you can see:</p>\n <ul>\n <li>We specify <code>image</code> as the image to embed.</li>\n <li>Parameters 2 and 3 specify the top-left corner of the slice to cut out of the source image, with the X value as <code>sprite</code> multiplied by 102 (where <code>sprite</code> is the sprite number between 0 and 5) and the Y value always 0.</li>\n <li>Parameters 4 and 5 specify the size of the slice to cut out — 102 pixels by 148 pixels.</li>\n <li>Parameters 6 and 7 specify the top-left corner of the box into which to draw the slice on the canvas — the X position is 0 + <code>posX</code>, meaning that we can alter the drawing position by altering the <code>posX</code> value.</li>\n <li>Parameters 8 and 9 specify the size of the image on the canvas. We just want to keep its original size, so we specify 102 and 148 as the width and height.</li>\n </ul>\n </li>\n <li>\n <p>Now we'll alter the <code>sprite</code> value after each draw — well, after some of them anyway. Add the following block to the bottom of the <code>draw()</code> function:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>if (posX % 13 === 0) {\n if (sprite === 5) {\n sprite = 0;\n } else {\n sprite++;\n }\n}\n</code></pre></div>\n <p>We are wrapping the whole block in <code>if (posX % 13 === 0) { }</code>. We use the modulo (<code>%</code>) operator (also known as the <a href=\"/en-US/docs/Web/JavaScript/Reference/Operators/Remainder\">remainder operator</a>) to check whether the <code>posX</code> value can be exactly divided by 13 with no remainder. If so, we move on to the next sprite by incrementing <code>sprite</code> (wrapping to 0 after we're done with sprite #5). This effectively means that we are only updating the sprite on every 13th frame, or roughly about 5 frames a second (<code>requestAnimationFrame()</code> calls us at up to 60 frames per second if possible). We are deliberately slowing down the frame rate because we only have six sprites to work with, and if we display one every 60th of a second, our character will move way too fast!</p>\n <p>Inside the outer block we use an <a href=\"/en-US/docs/Web/JavaScript/Reference/Statements/if...else\"><code>if...else</code></a> statement to check whether the <code>sprite</code> value is at 5 (the last sprite, given that the sprite numbers run from 0 to 5). If we are showing the last sprite already, we reset <code>sprite</code> back to 0; if not we just increment it by 1.</p>\n </li>\n <li>\n <p>Next we need to work out how to change the <code>posX</code> value on each frame — add the following code block just below your last one.</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>if (posX > width / 2) {\n let newStartPos = -(width / 2 + 102);\n posX = Math.ceil(newStartPos);\n console.log(posX);\n} else {\n posX += 2;\n}\n</code></pre></div>\n <p>We are using another <code>if...else</code> statement to see if the value of <code>posX</code> has become greater than <code>width/2</code>, which means our character has walked off the right edge of the screen. If so, we calculate a position that would put the character just to the left of the left side of the screen.</p>\n <p>If our character hasn't yet walked off the edge of the screen, we increment <code>posX</code> by 2. This will make him move a little bit to the right the next time we draw him.</p>\n </li>\n <li>\n <p>Finally, we need to make the animation loop by calling <a href=\"/en-US/docs/Web/API/Window/requestAnimationFrame\" title=\"requestAnimationFrame()\"><code>requestAnimationFrame()</code></a> at the bottom of the <code>draw()</code> function:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>window.requestAnimationFrame(draw);\n</code></pre></div>\n </li>\n</ol>\n<p>That's it! The final example should look like so:</p><iframe width=\"100%\" height=\"260\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> The finished code is available on GitHub as <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation\" class=\"external\" target=\"_blank\">7_canvas_walking_animation</a>.</p>\n</div>"}},{"type":"prose","value":{"id":"a_simple_drawing_application","title":"A simple drawing application","isH3":true,"content":"<p>As a final animation example, we'd like to show you a very simple drawing application, to illustrate how the animation loop can be combined with user input (like mouse movement, in this case). We won't get you to walk through and build this one; we'll just explore the most interesting parts of the code.</p>\n<p>The example can be found on GitHub as <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app\" class=\"external\" target=\"_blank\">8_canvas_drawing_app</a>, and you can play with it live below:</p><iframe width=\"100%\" height=\"600\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app/index.html\" loading=\"lazy\"></iframe>\n<p>Let's look at the most interesting parts. First of all, we keep track of the mouse's X and Y coordinates and whether it is being clicked or not with three variables: <code>curX</code>, <code>curY</code>, and <code>pressed</code>. When the mouse moves, we fire a function set as the <code>onmousemove</code> event handler, which captures the current X and Y values. We also use <code>onmousedown</code> and <code>onmouseup</code> event handlers to change the value of <code>pressed</code> to <code>true</code> when the mouse button is pressed, and back to <code>false</code> again when it is released.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let curX;\nlet curY;\nlet pressed = false;\n\n// update mouse pointer coordinates\ndocument.addEventListener(\"mousemove\", (e) => {\n curX = e.pageX;\n curY = e.pageY;\n});\n\ncanvas.addEventListener(\"mousedown\", () => (pressed = true));\n\ncanvas.addEventListener(\"mouseup\", () => (pressed = false));\n</code></pre></div>\n<p>When the \"Clear canvas\" button is pressed, we run a simple function that clears the whole canvas back to black, the same way we've seen before:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>clearBtn.addEventListener(\"click\", () => {\n ctx.fillStyle = \"rgb(0 0 0)\";\n ctx.fillRect(0, 0, width, height);\n});\n</code></pre></div>\n<p>The drawing loop is pretty simple this time around — if pressed is <code>true</code>, we draw a circle with a fill style equal to the value in the color picker, and a radius equal to the value set in the range input. We have to draw the circle 85 pixels above where we measured it from, because the vertical measurement is taken from the top of the viewport, but we are drawing the circle relative to the top of the canvas, which starts below the 85 pixel-high toolbar. If we drew it with just <code>curY</code> as the y coordinate, it would appear 85 pixels lower than the mouse position.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function draw() {\n if (pressed) {\n ctx.fillStyle = colorPicker.value;\n ctx.beginPath();\n ctx.arc(\n curX,\n curY - 85,\n sizePicker.value,\n degToRad(0),\n degToRad(360),\n false,\n );\n ctx.fill();\n }\n\n requestAnimationFrame(draw);\n}\n\ndraw();\n</code></pre></div>\n<p>All <a href=\"/en-US/docs/Web/HTML/Element/input\"><code><input></code></a> types are well supported. If a browser doesn't support an input type, it will fall back to a plain text fields.</p>"}},{"type":"prose","value":{"id":"webgl","title":"WebGL","isH3":false,"content":"<p>It's now time to leave 2D behind, and take a quick look at 3D canvas. 3D canvas content is specified using the <a href=\"/en-US/docs/Web/API/WebGL_API\">WebGL API</a>, which is a completely separate API from the 2D canvas API, even though they both render onto <a href=\"/en-US/docs/Web/HTML/Element/canvas\"><code><canvas></code></a> elements.</p>\n<p>WebGL is based on <a href=\"/en-US/docs/Glossary/OpenGL\">OpenGL</a> (Open Graphics Library), and allows you to communicate directly with the computer's <a href=\"/en-US/docs/Glossary/GPU\">GPU</a>. As such, writing raw WebGL is closer to low level languages such as C++ than regular JavaScript; it is quite complex but incredibly powerful.</p>"}},{"type":"prose","value":{"id":"using_a_library","title":"Using a library","isH3":true,"content":"<p>Because of its complexity, most people write 3D graphics code using a third party JavaScript library such as <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js\">Three.js</a>, <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas\">PlayCanvas</a>, or <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js\">Babylon.js</a>. Most of these work in a similar way, providing functionality to create primitive and custom shapes, position viewing cameras and lighting, covering surfaces with textures, and more. They handle the WebGL for you, letting you work on a higher level.</p>\n<p>Yes, using one of these means learning another new API (a third party one, in this case), but they are a lot simpler than coding raw WebGL.</p>"}},{"type":"prose","value":{"id":"recreating_our_cube","title":"Recreating our cube","isH3":true,"content":"<p>Let's look at an example of how to create something with a WebGL library. We'll choose <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js\">Three.js</a>, as it is one of the most popular ones. In this tutorial we'll create the 3D spinning cube we saw earlier.</p>\n<ol>\n <li>\n <p>To start with, make a local copy of <a href=\"https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/index.html\" class=\"external\" target=\"_blank\">threejs-cube/index.html</a> in a new folder, then save a copy of <a href=\"https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/metal003.png\" class=\"external\" target=\"_blank\">metal003.png</a> in the same folder. This is the image we'll use as a surface texture for the cube later on.</p>\n </li>\n <li>\n <p>Next, create a new file called <code>script.js</code>, again in the same folder as before.</p>\n </li>\n <li>\n <p>Next, you need to have the Three.js library installed. You can follow the environment setup steps described in the <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js\">Building up a basic demo with Three.js</a> so that you have Three.js working as expected.</p>\n </li>\n <li>\n <p>Now we've got <code>three.js</code> attached to our page, we can start to write JavaScript that makes use of it into <code>script.js</code>. Let's start by creating a new scene — add the following into your <code>script.js</code> file:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const scene = new THREE.Scene();\n</code></pre></div>\n <p>The <a href=\"https://threejs.org/docs/index.html#api/en/scenes/Scene\" class=\"external\" target=\"_blank\"><code>Scene()</code></a> constructor creates a new scene, which represents the whole 3D world we are trying to display.</p>\n </li>\n <li>\n <p>Next, we need a <strong>camera</strong> so we can see the scene. In 3D imagery terms, the camera represents a viewer's position in the world. To create a camera, add the following lines next:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const camera = new THREE.PerspectiveCamera(\n 75,\n window.innerWidth / window.innerHeight,\n 0.1,\n 1000,\n);\ncamera.position.z = 5;\n</code></pre></div>\n <p>The <a href=\"https://threejs.org/docs/index.html#api/en/cameras/PerspectiveCamera\" class=\"external\" target=\"_blank\"><code>PerspectiveCamera()</code></a> constructor takes four arguments:</p>\n <ul>\n <li>The field of view: How wide the area in front of the camera is that should be visible onscreen, in degrees.</li>\n <li>The <a href=\"/en-US/docs/Glossary/Aspect_ratio\">aspect ratio</a>: Usually, this is the ratio of the scene's width divided by the scene's height. Using another value will distort the scene (which might be what you want, but usually isn't).</li>\n <li>The near plane: How close to the camera objects can be before we stop rendering them to the screen. Think about how when you move your fingertip closer and closer to the space between your eyes, eventually you can't see it anymore.</li>\n <li>The far plane: How far away things are from the camera before they are no longer rendered.</li>\n </ul>\n <p>We also set the camera's position to be 5 distance units out of the Z axis, which, like in CSS, is out of the screen towards you, the viewer.</p>\n </li>\n <li>\n <p>The third vital ingredient is a renderer. This is an object that renders a given scene, as viewed through a given camera. We'll create one for now using the <a href=\"https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderer\" class=\"external\" target=\"_blank\"><code>WebGLRenderer()</code></a> constructor, but we'll not use it till later. Add the following lines next:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const renderer = new THREE.WebGLRenderer();\nrenderer.setSize(window.innerWidth, window.innerHeight);\ndocument.body.appendChild(renderer.domElement);\n</code></pre></div>\n <p>The first line creates a new renderer, the second line sets the size at which the renderer will draw the camera's view, and the third line appends the <a href=\"/en-US/docs/Web/HTML/Element/canvas\"><code><canvas></code></a> element created by the renderer to the document's <a href=\"/en-US/docs/Web/HTML/Element/body\"><code><body></code></a>. Now anything the renderer draws will be displayed in our window.</p>\n </li>\n <li>\n <p>Next, we want to create the cube we'll display on the canvas. Add the following chunk of code at the bottom of your JavaScript:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let cube;\n\nconst loader = new THREE.TextureLoader();\n\nloader.load(\"metal003.png\", (texture) => {\n texture.wrapS = THREE.RepeatWrapping;\n texture.wrapT = THREE.RepeatWrapping;\n texture.repeat.set(2, 2);\n\n const geometry = new THREE.BoxGeometry(2.4, 2.4, 2.4);\n const material = new THREE.MeshLambertMaterial({ map: texture });\n cube = new THREE.Mesh(geometry, material);\n scene.add(cube);\n\n draw();\n});\n</code></pre></div>\n <p>There's a bit more to take in here, so let's go through it in stages:</p>\n <ul>\n <li>We first create a <code>cube</code> global variable so we can access our cube from anywhere in the code.</li>\n <li>Next, we create a new <a href=\"https://threejs.org/docs/index.html#api/en/loaders/TextureLoader\" class=\"external\" target=\"_blank\"><code>TextureLoader</code></a> object, then call <code>load()</code> on it. <code>load()</code> takes two parameters in this case (although it can take more): the texture we want to load (our PNG), and a function that will run when the texture has loaded.</li>\n <li>Inside this function we use properties of the <a href=\"https://threejs.org/docs/index.html#api/en/textures/Texture\" class=\"external\" target=\"_blank\"><code>texture</code></a> object to specify that we want a 2 x 2 repeat of the image wrapped around all sides of the cube. Next, we create a new <a href=\"https://threejs.org/docs/index.html#api/en/geometries/BoxGeometry\" class=\"external\" target=\"_blank\"><code>BoxGeometry</code></a> object and a new <a href=\"https://threejs.org/docs/index.html#api/en/materials/MeshLambertMaterial\" class=\"external\" target=\"_blank\"><code>MeshLambertMaterial</code></a> object, and bring them together in a <a href=\"https://threejs.org/docs/index.html#api/en/objects/Mesh\" class=\"external\" target=\"_blank\"><code>Mesh</code></a> to create our cube. An object typically requires a geometry (what shape it is) and a material (what its surface looks like).</li>\n <li>Last of all, we add our cube to the scene, then call our <code>draw()</code> function to start off the animation.</li>\n </ul>\n </li>\n <li>\n <p>Before we get to defining <code>draw()</code>, we'll add a couple of lights to the scene, to liven things up a bit; add the following blocks next:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const light = new THREE.AmbientLight(\"rgb(255 255 255)\"); // soft white light\nscene.add(light);\n\nconst spotLight = new THREE.SpotLight(\"rgb(255 255 255)\");\nspotLight.position.set(100, 1000, 1000);\nspotLight.castShadow = true;\nscene.add(spotLight);\n</code></pre></div>\n <p>An <a href=\"https://threejs.org/docs/index.html#api/en/lights/AmbientLight\" class=\"external\" target=\"_blank\"><code>AmbientLight</code></a> object is a kind of soft light that lightens the whole scene a bit, like the sun when you are outside. The <a href=\"https://threejs.org/docs/index.html#api/en/lights/SpotLight\" class=\"external\" target=\"_blank\"><code>SpotLight</code></a> object, on the other hand, is a directional beam of light, more like a flashlight/torch (or a spotlight, in fact).</p>\n </li>\n <li>\n <p>Last of all, let's add our <code>draw()</code> function to the bottom of the code:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function draw() {\n cube.rotation.x += 0.01;\n cube.rotation.y += 0.01;\n renderer.render(scene, camera);\n\n requestAnimationFrame(draw);\n}\n</code></pre></div>\n <p>This is fairly intuitive; on each frame, we rotate our cube slightly on its X and Y axes, then render the scene as viewed by our camera, then finally call <code>requestAnimationFrame()</code> to schedule drawing our next frame.</p>\n </li>\n</ol>\n<p>Let's have another quick look at what the finished product should look like:</p><iframe width=\"100%\" height=\"500\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html\" loading=\"lazy\"></iframe>\n<p>You can <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-cube\" class=\"external\" target=\"_blank\">find the finished code on GitHub</a>.</p>\n<div class=\"notecard note\">\n <p><strong>Note:</strong> In our GitHub repo you can also find another interesting 3D cube example — <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-video-cube\" class=\"external\" target=\"_blank\">Three.js Video Cube</a> (<a href=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-video-cube/\" class=\"external\" target=\"_blank\">see it live also</a>). This uses <a href=\"/en-US/docs/Web/API/MediaDevices/getUserMedia\" title=\"getUserMedia()\"><code>getUserMedia()</code></a> to take a video stream from a computer web cam and project it onto the side of the cube as a texture!</p>\n</div>"}},{"type":"prose","value":{"id":"summary","title":"Summary","isH3":false,"content":"<p>At this point, you should have a useful idea of the basics of graphics programming using Canvas and WebGL and what you can do with these APIs, as well as a good idea of where to go for further information. Have fun!</p>"}},{"type":"prose","value":{"id":"see_also","title":"See also","isH3":false,"content":"<p>Here we have covered only the real basics of canvas — there is so much more to learn! The below articles will take you further.</p>\n<ul>\n <li><a href=\"/en-US/docs/Web/API/Canvas_API/Tutorial\">Canvas tutorial</a> — A very detailed tutorial series explaining what you should know about 2D canvas in much more detail than was covered here. Essential reading.</li>\n <li><a href=\"/en-US/docs/Web/API/WebGL_API/Tutorial\">WebGL tutorial</a> — A series that teaches the basics of raw WebGL programming.</li>\n <li><a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js\">Building up a basic demo with Three.js</a> — basic Three.js tutorial. We also have equivalent guides for <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas\">PlayCanvas</a> or <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js\">Babylon.js</a>.</li>\n <li><a href=\"/en-US/docs/Games\">Game development</a> — the landing page for web games development on MDN. There are some really useful tutorials and techniques available here related to 2D and 3D canvas — see the Techniques and Tutorials menu options.</li>\n</ul>"}},{"type":"prose","value":{"id":"examples","title":"Examples","isH3":false,"content":"<ul>\n <li><a href=\"https://github.com/mdn/webaudio-examples/tree/main/violent-theremin\" class=\"external\" target=\"_blank\">Violent theremin</a> — Uses the Web Audio API to generate sound, and canvas to generate a pretty visualization to go along with it.</li>\n <li><a href=\"https://github.com/mdn/webaudio-examples/tree/main/voice-change-o-matic\" class=\"external\" target=\"_blank\">Voice change-o-matic</a> — Uses a canvas to visualize real-time audio data from the Web Audio API.</li>\n</ul><ul class=\"prev-next\">\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs\"><span class=\"button-wrap\"> Previous </span></a></li>\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs\"><span class=\"button-wrap\"> Overview: Client-side web APIs</span></a></li>\n <li><a class=\"button secondary\" href=\"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs\"><span class=\"button-wrap\"> Next </span></a></li>\n</ul>"}}],"toc":[{"text":"Graphics on the Web","id":"graphics_on_the_web"},{"text":"Active learning: Getting started with a <canvas>","id":"active_learning_getting_started_with_a_canvas"},{"text":"2D canvas basics","id":"2d_canvas_basics"},{"text":"Loops and animations","id":"loops_and_animations"},{"text":"WebGL","id":"webgl"},{"text":"Summary","id":"summary"},{"text":"See also","id":"see_also"},{"text":"Examples","id":"examples"}],"summary":"At this point, you should have a useful idea of the basics of graphics programming using Canvas and WebGL and what you can do with these APIs, as well as a good idea of where to go for further information. Have fun!","popularity":0.0137,"modified":"2024-11-21T19:49:47.000Z","other_translations":[{"locale":"de","title":"Grafiken zeichnen","native":"Deutsch"},{"locale":"fr","title":"Dessiner des graphismes","native":"Français"},{"locale":"ja","title":"グラフィックの描画","native":"日本語"},{"locale":"zh-CN","title":"绘图","native":"中文 (简体)"}],"pageType":"learn-module-chapter","source":{"folder":"en-us/learn/javascript/client-side_web_apis/drawing_graphics","github_url":"https://github.com/mdn/content/blob/main/files/en-us/learn/javascript/client-side_web_apis/drawing_graphics/index.md","last_commit_url":"https://github.com/mdn/content/commit/93b34fcdb9cf91ff44f5dfe7f4dcd13e961962da","filename":"index.md"},"short_title":"Drawing graphics","parents":[{"uri":"/en-US/docs/Learn","title":"Guides"},{"uri":"/en-US/docs/Learn/JavaScript","title":"JavaScript — Dynamic client-side scripting"},{"uri":"/en-US/docs/Learn/JavaScript/Client-side_web_APIs","title":"Client-side web APIs"},{"uri":"/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics","title":"Drawing graphics"}],"pageTitle":"Drawing graphics - Learn web development | MDN","noIndexing":false}}</script></body></html>