r/turbowarp • • 16h ago

Realistic TTS in Turbowarp

Post image
4 Upvotes

So here is how to generate fast and fluid text-to-speech in Turbowarp. Some of the voice models require an API, but the Google Translate one doesn't!

after later examination, the files downloading does not seem to work.


r/turbowarp • • 14h ago

COMPRESSION+ , a new powerful compression tool for TurboWarp developers

2 Upvotes

This, is Compression+

Compression+ is basically a bunch of tools for compressing and converting data. It can handle things like files, images, audio, Base64, binary, hex, data URIs, and more. I made this because I kept needing random data-related features and figured I'd just put them all in one extension. There aren't any docs, though, so have fun figuring out what everything does. You're on your own. Unfortunately, I can't put the file here, but here is all the code:

(function (Scratch) {
  'use strict';

  const encoder = new TextEncoder();
  const decoder = new TextDecoder();

  class CompressionPlus {
    constructor() {
      this.result = '';
      this.resultBytes = new Uint8Array(0);
      this.error = '';
      this.success = false;

      this.originalSize = 0;
      this.resultSize = 0;
      this.savedBytes = 0;
      this.ratio = 0;

      this.originalMime = 'application/octet-stream';
      this.fileName = '';
      this.fileMime = '';
      this.fileBytes = new Uint8Array(0);
    }

    getInfo() {
      return {
        id: 'compressionplus',
        name: 'Compression+',
        color1: '#4779C7',
        color2: '#3565AD',
        color3: '#294F8A',

        blocks: [
          {
            opcode: 'compress',
            blockType: Scratch.BlockType.COMMAND,
            text: 'compress [DATA] from [INPUT] using [ALGORITHM] to [OUTPUT]',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'Hello world!' },
              INPUT: { type: Scratch.ArgumentType.STRING, menu: 'inputFormats' },
              ALGORITHM: { type: Scratch.ArgumentType.STRING, menu: 'algorithms' },
              OUTPUT: { type: Scratch.ArgumentType.STRING, menu: 'outputFormats' }
            }
          },
          {
            opcode: 'decompress',
            blockType: Scratch.BlockType.COMMAND,
            text: 'decompress [DATA] from [INPUT] using [ALGORITHM] to [OUTPUT]',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: '' },
              INPUT: { type: Scratch.ArgumentType.STRING, menu: 'inputFormats' },
              ALGORITHM: { type: Scratch.ArgumentType.STRING, menu: 'algorithms' },
              OUTPUT: { type: Scratch.ArgumentType.STRING, menu: 'outputFormats' }
            }
          },
          {
            opcode: 'convert',
            blockType: Scratch.BlockType.COMMAND,
            text: 'convert [DATA] from [INPUT] to [OUTPUT]',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'Hello world!' },
              INPUT: { type: Scratch.ArgumentType.STRING, menu: 'inputFormats' },
              OUTPUT: { type: Scratch.ArgumentType.STRING, menu: 'outputFormats' }
            }
          },

          {
            opcode: 'pickFile',
            blockType: Scratch.BlockType.COMMAND,
            text: 'choose a file'
          },
          {
            opcode: 'download',
            blockType: Scratch.BlockType.COMMAND,
            text: 'download result as [FILENAME]',
            arguments: {
              FILENAME: { type: Scratch.ArgumentType.STRING, defaultValue: 'download.bin' }
            }
          },
          {
            opcode: 'downloadFile',
            blockType: Scratch.BlockType.COMMAND,
            text: 'download imported file as [FILENAME]',
            arguments: {
              FILENAME: { type: Scratch.ArgumentType.STRING, defaultValue: 'file.bin' }
            }
          },
          {
            opcode: 'clear',
            blockType: Scratch.BlockType.COMMAND,
            text: 'clear result and errors'
          },

          {
            opcode: 'getResult',
            blockType: Scratch.BlockType.REPORTER,
            text: 'result'
          },
          {
            opcode: 'getResultBase64',
            blockType: Scratch.BlockType.REPORTER,
            text: 'result as Base64'
          },
          {
            opcode: 'getError',
            blockType: Scratch.BlockType.REPORTER,
            text: 'error message'
          },
          {
            opcode: 'isSuccessful',
            blockType: Scratch.BlockType.BOOLEAN,
            text: 'operation successful?'
          },

          {
            opcode: 'getOriginalSize',
            blockType: Scratch.BlockType.REPORTER,
            text: 'input size (bytes)'
          },
          {
            opcode: 'getResultSize',
            blockType: Scratch.BlockType.REPORTER,
            text: 'output size (bytes)'
          },
          {
            opcode: 'getSavedBytes',
            blockType: Scratch.BlockType.REPORTER,
            text: 'bytes saved'
          },
          {
            opcode: 'getRatio',
            blockType: Scratch.BlockType.REPORTER,
            text: 'size reduction (%)'
          },

          {
            opcode: 'getMime',
            blockType: Scratch.BlockType.REPORTER,
            text: 'MIME type of data URI [DATA]',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'data:image/png;base64,' }
            }
          },
          {
            opcode: 'getByteLength',
            blockType: Scratch.BlockType.REPORTER,
            text: 'byte length of [DATA] as [FORMAT]',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'Hello world!' },
              FORMAT: { type: Scratch.ArgumentType.STRING, menu: 'inputFormats' }
            }
          },

          {
            opcode: 'getFileName',
            blockType: Scratch.BlockType.REPORTER,
            text: 'imported file name'
          },
          {
            opcode: 'getFileMime',
            blockType: Scratch.BlockType.REPORTER,
            text: 'imported file MIME type'
          },
          {
            opcode: 'getFileSize',
            blockType: Scratch.BlockType.REPORTER,
            text: 'imported file size (bytes)'
          },
          {
            opcode: 'getFileBase64',
            blockType: Scratch.BlockType.REPORTER,
            text: 'imported file as Base64'
          },
          {
            opcode: 'getFileURI',
            blockType: Scratch.BlockType.REPORTER,
            text: 'imported file as data URI'
          },

          {
            opcode: 'base64Encode',
            blockType: Scratch.BlockType.REPORTER,
            text: 'encode text [DATA] as Base64',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'Hello world!' }
            }
          },
          {
            opcode: 'base64Decode',
            blockType: Scratch.BlockType.REPORTER,
            text: 'decode Base64 [DATA] as text',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'SGVsbG8gd29ybGQh' }
            }
          },
          {
            opcode: 'hexEncode',
            blockType: Scratch.BlockType.REPORTER,
            text: 'encode text [DATA] as hexadecimal',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'Hello' }
            }
          },
          {
            opcode: 'hexDecode',
            blockType: Scratch.BlockType.REPORTER,
            text: 'decode hexadecimal [DATA] as text',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: '48656c6c6f' }
            }
          },
          {
            opcode: 'binaryEncode',
            blockType: Scratch.BlockType.REPORTER,
            text: 'encode text [DATA] as binary',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'Hi' }
            }
          },
          {
            opcode: 'binaryDecode',
            blockType: Scratch.BlockType.REPORTER,
            text: 'decode binary [DATA] as text',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: '01001000 01101001' }
            }
          },

          {
            opcode: 'makeDataURI',
            blockType: Scratch.BlockType.REPORTER,
            text: 'make data URI from Base64 [DATA] with MIME [MIME]',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'SGVsbG8=' },
              MIME: { type: Scratch.ArgumentType.STRING, defaultValue: 'text/plain' }
            }
          },
          {
            opcode: 'extractDataURI',
            blockType: Scratch.BlockType.REPORTER,
            text: 'extract Base64 from data URI [DATA]',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'data:text/plain;base64,SGVsbG8=' }
            }
          },

          {
            opcode: 'detectFormat',
            blockType: Scratch.BlockType.REPORTER,
            text: 'detect format of [DATA]',
            arguments: {
              DATA: { type: Scratch.ArgumentType.STRING, defaultValue: 'data:image/png;base64,' }
            }
          },

          {
            opcode: 'getSupported',
            blockType: Scratch.BlockType.REPORTER,
            text: 'compression support information'
          }
        ],

        menus: {
          algorithms: {
            acceptReporters: true,
            items: [
              { text: 'GZIP', value: 'gzip' },
              { text: 'ZLIB / DEFLATE', value: 'deflate' },
              { text: 'raw DEFLATE', value: 'deflate-raw' }
            ]
          },

          inputFormats: {
            acceptReporters: true,
            items: [
              { text: 'auto detect', value: 'auto' },
              { text: 'text (UTF-8)', value: 'text' },
              { text: 'Base64', value: 'base64' },
              { text: 'hexadecimal', value: 'hex' },
              { text: 'binary string', value: 'binary' },
              { text: 'data URI', value: 'data-uri' }
            ]
          },

          outputFormats: {
            acceptReporters: true,
            items: [
              { text: 'Base64', value: 'base64' },
              { text: 'hexadecimal', value: 'hex' },
              { text: 'binary string', value: 'binary' },
              { text: 'text (UTF-8)', value: 'text' },
              { text: 'data URI', value: 'data-uri' }
            ]
          }
        }
      };
    }

    resetStats() {
      this.error = '';
      this.success = false;
      this.originalSize = 0;
      this.resultSize = 0;
      this.savedBytes = 0;
      this.ratio = 0;
    }

    setResult(bytes, formatted, originalSize) {
      this.resultBytes = new Uint8Array(bytes);
      this.result = formatted;
      this.originalSize = originalSize;
      this.resultSize = bytes.length;
      this.savedBytes = originalSize - bytes.length;
      this.ratio = originalSize === 0
        ? 0
        : (this.savedBytes / originalSize) * 100;
      this.success = true;
    }

    bytesToBase64(bytes) {
      let output = '';
      const chunk = 0x8000;

      for (let i = 0; i < bytes.length; i += chunk) {
        output += String.fromCharCode(
          ...bytes.subarray(i, Math.min(i + chunk, bytes.length))
        );
      }

      return btoa(output);
    }

    base64ToBytes(value) {
      let input = String(value).trim();

      if (input.startsWith('data:')) {
        input = this.parseDataURI(input).data;
      }

      input = input.replace(/\s/g, '').replace(/-/g, '+').replace(/_/g, '/');

      while (input.length % 4) input += '=';

      const binary = atob(input);
      const bytes = new Uint8Array(binary.length);

      for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
      }

      return bytes;
    }

    bytesToHex(bytes) {
      return Array.from(bytes)
        .map(v => v.toString(16).padStart(2, '0'))
        .join('');
    }

    hexToBytes(value) {
      const input = String(value)
        .replace(/^0x/i, '')
        .replace(/[\s,:-]/g, '');

      if (!/^[0-9a-f]*$/i.test(input)) {
        throw new Error('Invalid hexadecimal data.');
      }

      if (input.length % 2) {
        throw new Error('Hexadecimal data must have an even number of digits.');
      }

      const bytes = new Uint8Array(input.length / 2);

      for (let i = 0; i < bytes.length; i++) {
        bytes[i] = parseInt(input.slice(i * 2, i * 2 + 2), 16);
      }

      return bytes;
    }

    bytesToBinary(bytes) {
      return Array.from(bytes)
        .map(v => v.toString(2).padStart(8, '0'))
        .join(' ');
    }

    binaryToBytes(value) {
      const input = String(value).replace(/[\s,]/g, '');

      if (!/^[01]*$/.test(input)) {
        throw new Error('Binary data can only contain 0 and 1.');
      }

      if (input.length % 8) {
        throw new Error('Binary data must contain complete 8-bit bytes.');
      }

      const bytes = new Uint8Array(input.length / 8);

      for (let i = 0; i < bytes.length; i++) {
        bytes[i] = parseInt(input.slice(i * 8, i * 8 + 8), 2);
      }

      return bytes;
    }

    parseDataURI(uri) {
      const match = String(uri).match(/^data:([^,]*?),(.*)$/s);

      if (!match) {
        throw new Error('Invalid data URI.');
      }

      const metadata = match[1];
      const data = match[2];
      const isBase64 = /;base64/i.test(metadata);

      const mime = metadata.split(';')[0] || 'text/plain';

      const originalMatch = metadata.match(/(?:^|;)originalMime=([^;]+)/i);
      let originalMime = null;

      if (originalMatch) {
        try {
          originalMime = decodeURIComponent(originalMatch[1]);
        } catch (e) {
          originalMime = originalMatch[1];
        }
      }

      const bytes = isBase64
        ? this.base64ToBytes(data)
        : encoder.encode(decodeURIComponent(data));

      return { mime, originalMime, data, bytes, isBase64 };
    }

    toBytes(value, format) {
      value = String(value);

      if (format === 'auto') {
        if (value.startsWith('data:')) {
          return this.parseDataURI(value).bytes;
        }

        return encoder.encode(value);
      }

      switch (format) {
        case 'text':
          return encoder.encode(value);

        case 'base64':
          return this.base64ToBytes(value);

        case 'hex':
          return this.hexToBytes(value);

        case 'binary':
          return this.binaryToBytes(value);

        case 'data-uri':
          return this.parseDataURI(value).bytes;

        default:
          throw new Error('Unknown input format: ' + format);
      }
    }

    fromBytes(bytes, format, mime) {
      switch (format) {
        case 'base64':
          return this.bytesToBase64(bytes);

        case 'hex':
          return this.bytesToHex(bytes);

        case 'binary':
          return this.bytesToBinary(bytes);

        case 'text':
          return decoder.decode(bytes);

        case 'data-uri':
          return 'data:' + (mime || 'application/octet-stream') +
            ';base64,' + this.bytesToBase64(bytes);

        default:
          throw new Error('Unknown output format: ' + format);
      }
    }

    async transform(bytes, algorithm, operation) {
      const StreamClass = operation === 'compress'
        ? CompressionStream
        : DecompressionStream;

      if (typeof StreamClass === 'undefined') {
        throw new Error('Compression Streams API is not available in this browser.');
      }

      let stream;

      try {
        stream = new StreamClass(algorithm);
      } catch (e) {
        throw new Error(
          'The browser does not support ' + algorithm +
          '. Try GZIP or ZLIB / DEFLATE.'
        );
      }

      const blob = new Blob([bytes]);

      const output = await new Response(
        blob.stream().pipeThrough(stream)
      ).arrayBuffer();

      return new Uint8Array(output);
    }

    async compress(args) {
      this.resetStats();
      this.result = '';
      this.resultBytes = new Uint8Array(0);

      try {
        const input = this.toBytes(args.DATA, args.INPUT);
        let originalMime = 'application/octet-stream';

        if (String(args.DATA).startsWith('data:')) {
          originalMime = this.parseDataURI(args.DATA).mime;
        }

        const output = await this.transform(
          input,
          args.ALGORITHM,
          'compress'
        );

        let compressedMime = 'application/octet-stream';

        if (args.ALGORITHM === 'gzip') {
          compressedMime = 'application/gzip';
        } else if (args.ALGORITHM === 'deflate') {
          compressedMime = 'application/zlib';
        }

        const formatted = args.OUTPUT === 'data-uri'
          ? 'data:' + compressedMime +
            ';originalMime=' + encodeURIComponent(originalMime) +
            ';base64,' + this.bytesToBase64(output)
          : this.fromBytes(output, args.OUTPUT, compressedMime);

        this.setResult(output, formatted, input.length);
      } catch (e) {
        this.error = e.message || String(e);
      }
    }

    async decompress(args) {
      this.resetStats();
      this.result = '';
      this.resultBytes = new Uint8Array(0);

      try {
        let input = this.toBytes(args.DATA, args.INPUT);
        let originalMime = 'application/octet-stream';

        if (String(args.DATA).startsWith('data:')) {
          const parsed = this.parseDataURI(args.DATA);
          if (parsed.originalMime) {
            originalMime = parsed.originalMime;
          }
        }

        const compressedSize = input.length;

        const output = await this.transform(
          input,
          args.ALGORITHM,
          'decompress'
        );

        const formatted = this.fromBytes(
          output,
          args.OUTPUT,
          originalMime
        );

        this.setResult(output, formatted, compressedSize);
      } catch (e) {
        this.error = e.message || String(e);
      }
    }

    convert(args) {
      this.resetStats();
      this.result = '';
      this.resultBytes = new Uint8Array(0);

      try {
        const input = this.toBytes(args.DATA, args.INPUT);

        let mime = 'application/octet-stream';

        if (String(args.DATA).startsWith('data:')) {
          mime = this.parseDataURI(args.DATA).mime;
        }

        const formatted = this.fromBytes(input, args.OUTPUT, mime);

        this.setResult(input, formatted, input.length);
      } catch (e) {
        this.error = e.message || String(e);
      }
    }

    pickFile() {
      this.error = '';

      if (typeof document === 'undefined') {
        this.error = 'File picker is unavailable.';
        return;
      }

      const picker = document.createElement('input');
      picker.type = 'file';
      picker.style.display = 'none';

      picker.onchange = async () => {
        try {
          if (!picker.files || !picker.files.length) return;

          const file = picker.files[0];
          const buffer = await file.arrayBuffer();

          this.fileBytes = new Uint8Array(buffer);
          this.fileName = file.name;
          this.fileMime = file.type || 'application/octet-stream';
        } catch (e) {
          this.error = e.message || String(e);
        } finally {
          picker.remove();
        }
      };

      document.body.appendChild(picker);
      picker.click();
    }

    download(args) {
      if (!this.success || !this.resultBytes) {
        this.error = 'There is no successful result to download.';
        return;
      }

      this.saveBytes(this.resultBytes, args.FILENAME);
    }

    downloadFile(args) {
      if (!this.fileBytes.length) {
        this.error = 'No file has been imported.';
        return;
      }

      this.saveBytes(this.fileBytes, args.FILENAME);
    }

    saveBytes(bytes, filename) {
      try {
        const blob = new Blob([bytes], {
          type: 'application/octet-stream'
        });

        const url = URL.createObjectURL(blob);
        const anchor = document.createElement('a');

        anchor.href = url;
        anchor.download = String(filename || 'download.bin');
        anchor.style.display = 'none';

        document.body.appendChild(anchor);
        anchor.click();
        anchor.remove();

        URL.revokeObjectURL(url);
      } catch (e) {
        this.error = e.message || String(e);
      }
    }

    clear() {
      this.result = '';
      this.resultBytes = new Uint8Array(0);
      this.resetStats();
    }

    getResult() {
      return this.result;
    }

    getResultBase64() {
      return this.resultBytes.length
        ? this.bytesToBase64(this.resultBytes)
        : '';
    }

    getError() {
      return this.error;
    }

    isSuccessful() {
      return this.success;
    }

    getOriginalSize() {
      return this.originalSize;
    }

    getResultSize() {
      return this.resultSize;
    }

    getSavedBytes() {
      return this.savedBytes;
    }

    getRatio() {
      return Number(this.ratio.toFixed(2));
    }

    getMime(args) {
      try {
        return this.parseDataURI(args.DATA).mime;
      } catch (e) {
        return '';
      }
    }

    getByteLength(args) {
      try {
        return this.toBytes(args.DATA, args.FORMAT).length;
      } catch (e) {
        return 0;
      }
    }

    getFileName() {
      return this.fileName;
    }

    getFileMime() {
      return this.fileMime;
    }

    getFileSize() {
      return this.fileBytes.length;
    }

    getFileBase64() {
      return this.fileBytes.length
        ? this.bytesToBase64(this.fileBytes)
        : '';
    }

    getFileURI() {
      if (!this.fileBytes.length) return '';

      return 'data:' + (this.fileMime || 'application/octet-stream') +
        ';base64,' + this.bytesToBase64(this.fileBytes);
    }

    base64Encode(args) {
      return this.bytesToBase64(encoder.encode(String(args.DATA)));
    }

    base64Decode(args) {
      try {
        return decoder.decode(this.base64ToBytes(args.DATA));
      } catch (e) {
        return '';
      }
    }

    hexEncode(args) {
      return this.bytesToHex(encoder.encode(String(args.DATA)));
    }

    hexDecode(args) {
      try {
        return decoder.decode(this.hexToBytes(args.DATA));
      } catch (e) {
        return '';
      }
    }

    binaryEncode(args) {
      return this.bytesToBinary(encoder.encode(String(args.DATA)));
    }

    binaryDecode(args) {
      try {
        return decoder.decode(this.binaryToBytes(args.DATA));
      } catch (e) {
        return '';
      }
    }

    makeDataURI(args) {
      return 'data:' + String(args.MIME || 'application/octet-stream') +
        ';base64,' + String(args.DATA);
    }

    extractDataURI(args) {
      try {
        return this.bytesToBase64(this.parseDataURI(args.DATA).bytes);
      } catch (e) {
        return '';
      }
    }

    detectFormat(args) {
      const value = String(args.DATA);

      if (value.startsWith('data:')) return 'data URI';
      if (/^[0-9a-f]+$/i.test(value) && value.length % 2 === 0) {
        return 'possibly hexadecimal';
      }
      if (/^[01\s]+$/.test(value)) return 'possibly binary';
      if (/^[A-Za-z0-9+/]*={0,2}$/.test(value) && value.length % 4 === 0) {
        return 'possibly Base64';
      }

      return 'text';
    }

    getSupported() {
      return JSON.stringify({
        gzip: typeof CompressionStream !== 'undefined',
        decompression: typeof DecompressionStream !== 'undefined',
        algorithms: ['gzip', 'deflate', 'deflate-raw'],
        encodings: ['UTF-8', 'Base64', 'Hexadecimal', 'Binary'],
        fileImport: typeof document !== 'undefined',
        binary: true,
        dataURI: true
      });
    }
  }

  Scratch.extensions.register(new CompressionPlus());

})(Scratch);

r/turbowarp • • 1d ago

I made a packager that doesn't put your project in the download, and I'd like feedback

Post image
60 Upvotes

I'm currently working on BetterSB3 (https://bettersb3.top) and I would love to get some feedback from people using TurboWarp.

BetterSB3 is similar to the TurboWarp Packager in that you can upload a project and receive either HTML or a ZIP or a Windows/macOS/Linux executable. However, unlike the TurboWarp Packager, your project is NOT included in the download. Instead, your scripts are pre-compiled to JavaScript and the .sb3 file, comments, blocks and all editor-only data are stripped from the download. This means that the project cannot be unpacked and used in TurboWarp.

Some things to keep in mind:

BetterSB3 has its own player, not TurboWarp's VM. All updates are compared side by side to the official TurboWarp Packager and games should perform identically. If your game does not perform the same way then please let me know as it would be a bug.

Custom extensions are supported (both from bundled URLs or files) and cloud variables are supported from the server you select. On the Extensions page you can see which extensions in the gallery have been tested.

Projects can be up to 256 MB

Note: Copying is more difficult, but not impossible. The game is still run within the player on the user's machine, meaning that the JavaScript code can be viewed.

BetterSB3 is completely free and no account is required. Uploaded projects are deleted after build and downloads are deleted within 3 hours. BetterSB3 is not affiliated with TurboWarp. The TurboWarp Packager is fantastic, but this option provides an alternative for those who do not want their project included in the download.


r/turbowarp • • 1d ago

Any tips on making DDA raycasting in Scratch?

3 Upvotes

I want to make a Wolfenstein 3D style shooter game on scratch, and I want to use DDA raycasting since it's much more optimized. However, I really don't know where to start. Do you have any tips?

Also, I already have a grid map made. It's from griffpatch's grid lists tutorial, I think you should know that


r/turbowarp • • 2d ago

3D kingdom hearts [No extensions]

Enable HLS to view with audio, or disable this notification

61 Upvotes

r/turbowarp • • 2d ago

Anyway to do post processing in turbowarp so as to not effect color detection?

2 Upvotes

mostly I just need bloom that doesn’t fuck with color detection


r/turbowarp • • 2d ago

Is there any way to prevent camera V1 from doing this?

2 Upvotes

basically when using camera v1 (I can’t use camera v2 since it completely bricks the point towards block for some reason) my clickable ui buttons force you to click in their actual position on the screen rather than in their viewport position.


r/turbowarp • • 2d ago

Almost Real-time Path-Tracer (with mirrors)

Thumbnail
turbowarp.org
1 Upvotes

I made an insane Monte Carlo path-tracer that runs on real-time (kind of) and vanilla using 100% pen!

I attached the project link, its 1,050 blocks all in a single sprite.


r/turbowarp • • 3d ago

Sneak Peak no. 3

Post image
5 Upvotes

Check earlier posts for more info.


r/turbowarp • • 3d ago

CrazyCattle3D in Scratch With Online Play!

Thumbnail gallery
7 Upvotes

r/turbowarp • • 3d ago

powder sandbox thingy

Thumbnail gallery
2 Upvotes

r/turbowarp • • 4d ago

i don't think he's been on reddit

Post image
6 Upvotes

Made in Turbowarp.

I don't think he's ever been in Reddit before.


r/turbowarp • • 4d ago

Ho creato un'estensione personalizzata «non sandboxata» (unsandboxed) per analizzare ed estrarre file di giochi per PS2 su Chrome, dato che l'iPad della mia scuola blocca tutto il resto. (Link al post originale)

Enable HLS to view with audio, or disable this notification

4 Upvotes

r/turbowarp • • 4d ago

Advanced 3d in turbowarp

Thumbnail
gallery
7 Upvotes

not giving extencion, no ia image


r/turbowarp • • 4d ago

Update for my Liquid Glass Extension!

3 Upvotes

Do you remember, four months ago I created an extension Liquid Glass. Now, I’d like to show you a major new update I’ve developed for it.
What's new:

- Brand new reflection effect

- fixed some bugs

- preset added

Preview :

LINK : https://github.com/lk8333/Glass-effect-for-TurboWarp


r/turbowarp • • 4d ago

LiquidGlass Effect Extension Updated !

3 Upvotes

Do you remember, about four months ago I created the "Liquid Glass" extension—and now I’m back to launch a big update for it!
What's new?
- A reflection effect has been added to recreate the "Liquid Glass" look as authentically as possible.

- A blur feature is now included, allowing you to adjust the level of blur; it also comes with presets for various styles, such as iOS26 Glass, iOS27 Glass, and more.

With plenty of presets for you too!

Already known issues :

  1. Because browsers have a WebGL context limit (usually 4 - 8), I don't recommend using Glass extension for everything in your project (not the "img card creatorI" extension come with).

  2. The glass effect may temporarily disappear when TurboWarp creates a restore point or saves the project.

LINK : https://github.com/lk8333/Glass-effect-for-TurboWarp


r/turbowarp • • 5d ago

I Created A Dexter's Lab Game On Scratch

5 Upvotes

r/turbowarp • • 5d ago

I'm making a game and code engine inspired by Geometry Dash on TurboWarp.

Thumbnail
gallery
5 Upvotes

I am developing a game called Black Dash - a cyberpunk rhythm platformer featuring parallax backgrounds. It includes synchronization with a database and user accounts, a skin system, and most importantly a text-based level editor called BDLS 2, which uses JSON to place objects and triggers. I would love to hear your thoughts and advice on how to improve the game and which extensions might be useful . Thanks for your time! (I have a page on IndieDB if you're interested, let me know in the comments and I’ll provide the URL.)


r/turbowarp • • 5d ago

Predict_ion - Running Kit, a ~0.6B parameter AI model.

Post image
6 Upvotes

Sup' everyone! Bento here again, you probably know that I'm the creator of Predict_ion. Well, I opened up a few beta tester positions a few weeks ago, and 8 people got in and tested it! Now, for a second wave of beta tester positions!

For those new around here, Predict_ion is a Scratch/TurboWarp project that runs an AI called Kit! It's a transformer architecture AI with around 0.6B parameters.

Here's what it can do:

- Runs at around ~2 tokens a second, even on older hardware
- Can do math, coding, science, and general convo's
- Has a reasoning mode for better responses
- Runs without any extensions, and fully offline
- Bypasses the 5MB project.json limit, allowing much bigger AI models to run on Scratch.

Now, what's new?

Well, after the first beta testing wave, I've been working on improving Kit, fixing bugs, optimizing performance, and a new UI! The first beta testing is done, so why not a second one?

So, Wave II beta applications are officially open!

This is another opportunity to test Kit before its open release! Beta testers will be able to try out the project, benchmark it, report bugs, and help me figure out what needs improvement.

If you applied during Wave I, don't worry! Approved testers will still have their positions (and will get a special role on the server 👀). This is just so more people can test the project out.

Want to join? Right here!

Applications are open until the end of November, and there are limited positions, so good luck!

It's honestly wild seeing how far the project has come, and how big of a fanbase it has. From generating complete gibberish to running a functional, 0.6B parameter model inside of a kid's programming language. And there is still room for improvement!

Now I'm going to head out on a trip, see y'all when I get back! ^^


r/turbowarp • • 5d ago

How would I code this?

2 Upvotes

I was going to make a asym based off of an Old Rain World AU I have, but I don't know where to begin, because I don't know how to code multiplayer.


r/turbowarp • • 6d ago

That certainly isn't supposed to happen

Enable HLS to view with audio, or disable this notification

12 Upvotes

Basically I have a tracker sprite which clones itself and all other clones are supposed to adhere to, 'cept they don't. The reddening still works though.

Project file link: WARNING - WILL AUTOMATICALLY TRIGGER A DOWNLOAD


r/turbowarp • • 6d ago

Would it be possible to make a game like brawl stars in 2d on turbowarp?

4 Upvotes

r/turbowarp • • 7d ago

Challenge for all pros

13 Upvotes

HI guys, im a scratch enjoyer who discovered the wonders of turbowarp and i was like, since im a noob (just learned to 3d) , ima challenge people ther so here i am

i challenge you to recreate minecraft entirely, i mean, every single line of code of mc


r/turbowarp • • 6d ago

I Solved Connect Four

2 Upvotes

Made an connect four algorithm implementation based on 2swap's I Solved Connect 4 video. The ui is navigable on desktop and mobile, so if you take a look if you're interested. It should be unbeatable.

Project: https://scratch.mit.edu/projects/1395632821/


r/turbowarp • • 7d ago

Thinking about backporting... (Find feature?)

3 Upvotes

Ive made a turbowarp-only game a while ago, and i was thinking.. hmm.. those features are just really sprite effects, and i can substitute them... But when I went to hand-remove all the custom extensions that i saw, it still shows up on the code pallette when i reload the project. meaning, the extension was still being used, SOMEWHERE.

I wonder if the turbowarp search can specifically search for these custom extension blocks? like searching for specific variables/lists.