Skip to main content

Rebranding the Editor

You probably want to give your mod a unique look, here are some things you can do to do that.

warning

To an extent, you must rebrand your mod as you can't use Scratch's trademarks. Scratch's trademarks are the name Scratch, the Scratch logo, the Scratch Day logo, Scratch Cat and Gobo. However, you can use them to give credit to the original Scratch.

Scratch Terms of Use

5.4 The Scratch name, Scratch logo, Scratch Day logo, Scratch Cat, and Gobo are Trademarks owned by the Scratch Team. The MIT name and logo are Trademarks owned by the Massachusetts Institute of Technology. Unless you are licensed by Scratch under a specific licensing program or agreement, you may not use these logos to label, promote, or endorse any product or service. You may use the Scratch Logo to refer to the Scratch website and programming language.

You can change the logo by replacing the SVG located at scratch-gui/src/components/menu-bar/scratch-logo.svg.

Changing the default project (including Scratch Cat)

The default project is located at scratch-gui/src/lib/default-project.

FileFormatContents
0fb9be3e8397c983338cb71dc84d0b25.svgSVG Imagecostume2 (Scratch Cat)
83a9787d4cb6f3b7632b4ddfebf74367.wavWave Soundpop
83c36d806dc92327b9e7049a565c6bff.wavWave SoundMeow
bcf454acf82e4504149f7ffe07081dbc.svgSVG Imagecostume1 (Scratch Cat)
cd21514d0531fdffb22204e0ec5ed84a.svgSVG Imagebackdrop1
index.jsJavaScriptList of contents of the project.
projectData.jsJavaScriptContains the project.json.

Changing the colours

Scratch's colours are defined in scratch-gui/src/css/colors.css. Below is a table of all the colours and a screenshot of the editor showing an example of where that colour is used. These screenshots do not include all uses of the colour.

Variable NameDefault ValueUsage Screenshot
$ui-primary
hsla(215, 100%, 95%, 1)Screenshot
$ui-secondary
hsla(215, 75%, 95%, 1)Screenshot
$ui-tertiary
hsla(215, 50%, 90%, 1)Screenshot
$ui-motion-overlay
hsla(215, 100%, 65%, 0.9)Screenshot
$ui-white
hsla(0, 100%, 100%, 1)Screenshot
$ui-white-dim
hsla(0, 100%, 100%, 0.75)Screenshot
$ui-white-transparent
hsla(0, 100%, 100%, 0.25)Screenshot
$ui-transparent
hsla(0, 100%, 100%, 0)Screenshot
$ui-black-transparent
hsla(0, 0%, 0%, 0.15)Screenshot
$text-primary
hsla(225, 15%, 40%, 1)Screenshot
$text-primary-transparent
hsla(225, 15%, 40%, 0.75)Screenshot
$motion-primary
hsla(215, 100%, 65%, 1)Screenshot (also loading screen)
$motion-tertiary
hsla(215, 60%, 50%, 1)Unused
$looks-primary
hsla(260, 60%, 60%, 1)Screenshot
$looks-transparent
hsla(260, 60%, 60%, 0.35)Screenshot
$looks-light-transparent
hsla(260, 60%, 60%, 0.15)Screenshot
$looks-secondary-dark
hsla(260, 42%, 51%, 1)Screenshot
$red-primary
hsla(20, 100%, 55%, 1)Screenshot
$red-tertiary
hsla(20, 100%, 45%, 1)Screenshot
$sound-primary
hsla(300, 53%, 60%, 1)Screenshot
$sound-tertiary
hsla(300, 48%, 50%, 1)Screenshot
$control-primary
hsla(38, 100%, 55%, 1)Screenshot
$data-primary
hsla(30, 100%, 55%, 1)Screenshot
$pen-primary
hsla(163, 85%, 40%, 1)Screenshot
$pen-transparent
hsla(163, 85%, 40%, 0.25)Screenshot
$pen-tertiary
hsla(163, 86%, 30%, 1)Screenshot
$error-primary
hsla(30, 100%, 55%, 1)Screenshot
$error-light
hsla(30, 100%, 70%, 1)No screenshot.
$error-transparent
hsla(30, 100%, 55%, 0.25)Screenshot
$extensions-primary
hsla(163, 85%, 40%, 1)Screenshot
$extensions-tertiary
hsla(163, 85%, 30%, 1)No screenshot.
$extensions-transparent
hsla(163, 85%, 40%, 0.35)No screenshot.
$extensions-light
hsla(163, 57%, 85%, 1)No screenshot.
$drop-highlight
hsla(215, 100%, 77%, 1)Screenshot

Changing the project file extension

There are a few places you need to change to adjust the project file extension.

  • scratch-gui/src/containers/sb3-downloader.jsx
    const getProjectFilename = (curTitle, defaultTitle) => {
    let filenameTitle = curTitle;
    if (!filenameTitle || filenameTitle.length === 0) {
    filenameTitle = defaultTitle;
    }
    return `${filenameTitle.substring(0, 100)}.sb3`;
    };
    Adjust the .sb3 with your own extension.
  • scratch-gui/src/lib/sb-file-uploader-hoc.jsx
    createFileObjects () {
    // redo step 7, in case it got skipped last time and its objects are
    // still in memory
    this.removeFileObjects();
    // create fileReader
    this.fileReader = new FileReader();
    this.fileReader.onload = this.onload;
    // create <input> element and add it to DOM
    this.inputElement = document.createElement('input');
    this.inputElement.accept = '.sb,.sb2,.sb3';
    this.inputElement.style = 'display: none;';
    this.inputElement.type = 'file';
    this.inputElement.onchange = this.handleChange; // connects to step 3
    document.body.appendChild(this.inputElement);
    // simulate a click to open file chooser dialog
    this.inputElement.click();
    }
    Add your extension to the list of accepted extensions.
  • scratch-gui/src/lib/sb-file-uploader-hoc.jsx
    getProjectTitleFromFilename (fileInputFilename) {
    if (!fileInputFilename) return '';
    // only parse title with valid scratch project extensions
    // (.sb, .sb2, and .sb3)
    const matches = fileInputFilename.match(/^(.*)\.sb[23]?$/);
    if (!matches) return '';
    return matches[1].substring(0, 100); // truncate project title to max 100 chars
    }
    You will need to adjust the regular expression used. Here is a template you can use: /^(.*)\.(sb[23]?|YOUR_EXTENSION_HERE)$/.
  • scratch-vm/src/virtual-machine.js
    /**
    * @returns {string} Project in a Scratch 3.0 JSON representation.
    */
    saveProjectSb3 () {
    const soundDescs = serializeSounds(this.runtime);
    const costumeDescs = serializeCostumes(this.runtime);
    const projectJson = this.toJSON();

    // TODO want to eventually move zip creation out of here, and perhaps
    // into scratch-storage
    const zip = new JSZip();

    // Put everything in a zip file
    zip.file('project.json', projectJson);
    this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip);

    return zip.generateAsync({
    type: 'blob',
    mimeType: 'application/x.scratch.sb3',
    compression: 'DEFLATE',
    compressionOptions: {
    level: 6 // Tradeoff between best speed (1) and best compression (9)
    }
    });
    }
    This is not directly related to the project extension, but you should adjust it still. You should keep the start of the MIME type the same (application/x.).

Changing the sprite file extension

There are a few places you need to change to adjust the sprite file extension.

  • scratch-gui/src/components/sprite-selector/sprite-selector.jsx
    <ActionMenu
    className={styles.addButton}
    img={spriteIcon}
    moreButtons={[
    {
    title: intl.formatMessage(messages.addSpriteFromFile),
    img: fileUploadIcon,
    onClick: onFileUploadClick,
    fileAccept: '.svg, .png, .bmp, .jpg, .jpeg, .sprite2, .sprite3, .gif',
    fileChange: onSpriteUpload,
    fileInput: spriteFileInput,
    fileMultiple: true
    }, {
    title: intl.formatMessage(messages.addSpriteFromSurprise),
    img: surpriseIcon,
    onClick: onSurpriseSpriteClick // TODO need real function for this
    }, {
    title: intl.formatMessage(messages.addSpriteFromPaint),
    img: paintIcon,
    onClick: onPaintSpriteClick // TODO need real function for this
    }, {
    title: intl.formatMessage(messages.addSpriteFromLibrary),
    img: searchIcon,
    onClick: onNewSpriteClick
    }
    ]}
    title={intl.formatMessage(messages.addSpriteFromLibrary)}
    tooltipPlace={isRtl(intl.locale) ? 'right' : 'left'}
    onClick={onNewSpriteClick}
    />
    Add your sprite file extension to fileAccept.
  • scratch-gui/src/containers/target-pane.jsx
    handleExportSprite (id) {
    const spriteName = this.props.vm.runtime.getTargetById(id).getName();
    const saveLink = document.createElement('a');
    document.body.appendChild(saveLink);

    this.props.vm.exportSprite(id).then(content => {
    downloadBlob(`${spriteName}.sprite3`, content);
    });
    }
    Change sprite3 to your sprite file extension.
  • scratch-vm/src/virtual-machine.js
    exportSprite (targetId, optZipType) {
    const soundDescs = serializeSounds(this.runtime, targetId);
    const costumeDescs = serializeCostumes(this.runtime, targetId);
    const spriteJson = this.toJSON(targetId);

    const zip = new JSZip();
    zip.file('sprite.json', spriteJson);
    this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip);

    return zip.generateAsync({
    type: typeof optZipType === 'string' ? optZipType : 'blob',
    mimeType: 'application/x.scratch.sprite3',
    compression: 'DEFLATE',
    compressionOptions: {
    level: 6
    }
    });
    }
    This is not directly related to the sprite extension, but you should adjust it still. You should keep the start of the MIME type the same (application/x.).

Scratch commit hashes at the time of this tutorial
scratch-gui:        db1933b2aea9f9dbe51e0ad2d750a2550179314a
scratch-vm: d6420c4c9826d360adee118e0e9e255536be7f7c
scratch-blocks: 686df65cc6b5b3df37dc3204f56f443aa18c5085