leopard-js / leopard Goto Github PK
View Code? Open in Web Editor NEWLibrary for making Scratch-like projects with JavaScript.
Home Page: https://leopardjs.com
License: MIT License
Library for making Scratch-like projects with JavaScript.
Home Page: https://leopardjs.com
License: MIT License
Doing it server-side keeps giving me a 502 error. I think you should zip it client-side using JSZip.
Pretty straightforward. Arrow keys, space, etc... shouldn't perform the default action when project is focused.
Thanks to s_federici for pointing this out. :)
Currently, Costume
s don't appear until their images are loaded, and sounds only play once they're loaded.
There are some APIs that would greatly benefit from Costume
s' images being loaded at construction-time, and projects not being "really loaded" until some inteterminate point in the future is not ideal.
To fix this, there would need to be some sort of API for loading a project and all its assets, but I'm not sure what that would look like.
Because the say
and think
functions' argument isn't cast to a string, "falsy" values like false
and 0
will cause the text bubble to disappear.
To support layer ordering, we'll need to do one of two things:
Project.sprites
from an object (which has no explicit order) to an Array, and add a getSprite
or sprite
method to access a sprite by name.layerOrder
property, and use some complex logic like Scratch does to keep them in order in the renderer. Some of this logic (e.g. sorting sprites by layer order) would have to be done each frame, as we don't maintain a "draw list", instead just drawing whatever Project.sprites
contains.Originally posted by @adroitwhiz in #23 (comment)
See #29 (comment)
Fancy development tools aren't strictly required to work on scratch-js, but eslint and prettier are both nice-to-haves. This can be indicated in the devDependencies of a package.json at the root.
While we're at it, there is also a vscode-specific file we can add to recommend certain vscode extensions for this editing repository (such as the prettier extension).
Right now there's an API in place for replicating Scratch's variables, but it has some problems. The way it works right now is that scripts run by a trigger are passed two variables: one object containing the global variables, and another containing the sprite variables. Unfortunately, this setup means that variables are not currently accessible from within custom blocks (methods) unless they are passed manually. This is really bad.
We need a better system.
In Scratch, there are a variety of variable types:
(Per-script variables don't exist in Scratch like they do in Snap!, but they should in scratch-js and will exist essentially by default.)
Cloud variables rely on server infrastructure to operate, so they probably don't make sense to support in scratch-js, at least in the beginning.
This leaves us with variables and lists at the sprite and stage level. There are few options to consider:
Is there some form of documentation in progress offline or has that not yet begun? The functions seem fairly straightforward, but a documentation would also be helpful. If necessary, I can start detailing them (kind of like how blocks are documented on the Scratch Wiki).
Whenever I have tried to convert a scratch project (I have tried it with three different projects from different people, all public), it fails, downloading "codesandbox.json" and having the error "server problem", on at least two different browsers. Any help to get this to work would be greatly appreciated.
When I click on "Edit as javascript" for project https://scratch.mit.edu/projects/345789566/
I get
{"errors":{"detail":["Bad request"]}}
Haven't looked into it closely, but this project works differently after being converted to scratch-js: https://scratch.mit.edu/projects/387687601/
Could also be an sb-edit issue. Not sure.
(Thanks to TheGoldstar16 for reporting!)
I tried with https://scratch.mit.edu/projects/312769512/. I get a black screen at https://leopardjs.now.sh/?id=312769512
To match Scratch behavior, creating a clone of a clone currently does a sneaky bait-and-switch where the original sprite is cloned again instead.
As pointed out by @nanaian, this is not very friendly to the honorable individuals who choose to use scratch-js.
A better option would be to store clone instances in a properly nested manner, but update sb-edit, the Scratch project translator, to always create a clone of the original when create clone of [myself v]
is invoked. That would look like this.sprites.sprite1.createClone()
.
https://scratch.mit.edu/projects/345789566/#comments-130544364
Okay, On chrome on my mac: Sprites shrink down to the bottom left corner of the display box, a quarter of the size. Pen covers the entire screen, however. keyboard scroll controls are not locked. Although you may be aware of some of these.
On further inspection, some projects have improperly displayed sprites than others for some reason
Here's what I tested: https://scratch-js.now.sh/?id=382544425 , https://scratch-js.now.sh/?id=380380623
Not sure if this is an issue with scratch-js or sb-edit.
Building off of #18 but I figure it makes sense to track these separately (requires more work in sb-edit than here, and not exactly part of the 'standardized' blocks of Scratch 3.0).
Some projects depend on obsolete Scratch blocks, such as "for each (variable) in (list)". In order to support all projects that can be run by Scratch 3.0, we should look into supporting such blocks — I'd be up for trying that out since I'd like to get familiar with the codebase anyway!
I believe we'd need to update sb-edit to support these blocks as well; most of them don't add any complex new behavior (and some just don't do anything, but still arguably oughtn't crash sb-edit), so most work would be tied to implementing their opcode/block definitions and writing export-related code in toScratchJS.ts
.
I've got experience implementing most of these blocks - check it out: scratchfoundation/scratch-vm#355 (comment). All those PRs are by me! :P
Branching off of #18 and building on my work in #52 and various PRs implementing or updating blocks, I'd like to personally work on implementing audio effects. Here's the todo list:
this.setAudioEffect("pan", 50)
for instance (I'm considering putting volume in the same API as other audio effects, to keep things simple)Implementing all of this would, in combination with #52, complete support for sound compatibility between Scratch 3.0 and scratch-js (excluding the Instruments extension).
I'd study and implement code as identical as possible to the behavior covered in the "effects" directory of scratch-audio, the engine built for playing sound in Scratch.
(An alternative route to this goal would be implementing the scratch-audio library itself into scratch-js, but scratch-js as of yet has no dependencies. Also, we implement our own rendering system rather than using scratch-render—our rendering system was made by @adroitwhiz, who's very knowledgeable of Scratch 3.0's rendering—so it would make sense to do the same for audio.)
/cc @PullJosh - Does this sound like a good plan to you? Also, I don't know if it's possible to assign an issue to a non-collaborator; if so, I'd appreciate the assignment, so that the issue list is kept organized.
Right now, the broadcast
and broadcastAndWait
blocks don't work correctly when they call each other. The goal is to perfectly replicate Scratch's behavior.
I've been struggling to make it work myself. If someone could help debug this one, I'd be thrilled.
(Fork from the broadcasts branch and then submit a PR.)
Currently, most operators are using JS syntaxes. However, Scratch handles them differently:
(0/0) == (0/0)
tan
implementationShould scratch-js implement those?
When I wrote the WebGL renderer, the behavior of "touching color (white)" when a sprite is over a transparent part of the stage's backdrop was not specified in Scratch itself. Because the color that appears under transparent parts of the backdrop is white, it would seem that "touching color (white)" would return true in such a case, but that wasn't how Scratch 2.0 behaved, and it was only in scratchfoundation/scratch-render#489 that it was decided that 3.0 would break compatibility with 2.0 and return true.
Now that the behavior is specified in 3.0, Leopard should match it.
Turbowarp works well with raytracers, raymarchers, etc. But then I wanted to try one on leopard, and to my surprise, it was pretty slow, probably because the fps was locked to 60, could a turbo mode be added that unlocks the fps?
As to projects that do not translate, e.g. the ones listed at #74, are there any cue as to where to start looking at?
Scratch allows you to set a sprite's rotation style to "all around", "left-right", and "don't rotate".
I can think of two ways to implement this in scratch-js. One is to use string keys:
this.rotationStyle = "all around";
this.rotationStyle = "left-right";
this.rotationStyle = "don't rotate"
The other is to use an enum:
// these don't have to be Symbols; the important thing is they're stored in an enum
Sprite.RotationStyle = {
AllAround: Symbol("all around"),
LeftRight: Symbol("left-right"),
DontRotate: Symbol("don't rotate")
}
which could be used like so:
this.rotationStyle = Sprite.RotationStyle.AllAround;
this.rotationStyle = Sprite.RotationStyle.LeftRight;
this.rotationStyle = Sprite.RotationStyle.DontRotate;
I think the second option is less error-prone, but it would be the first time a static property is added to the Sprite
class.
There are two different stage sizes that are relevant here: the Scratch-space size of the stage (what the sprites' X and Y values are in), and the rendered size of the stage (which can differ in e.g. fullscreen or small-stage mode).
It looks like the stage is currently always initialized with a Scratch-space and screen-space size of 480x360. I'm wondering about the design for two APIs:
EDIT: Tracking progress:
Incorporating WebGL in some form is necessary to implement graphic effects at a reasonable speed.
I was thinking of rewriting the renderer to use WebGL at its core, similar to scratch-render. This could potentially take care of a number of currently missing blocks in one fell swoop:
There are a couple of implementation details I'm not sure about, though:
In regular Scratch, each sprite has a fixed number of costumes, and you can only create costumes by clicking a button in the editor. In scratch-js, on the other hand, you can dynamically instantiate as many Costume
objects as you like.
This presents a problem because WebGL textures are never garbage-collected. In Scratch, the textures that back a costume are deleted when the costume is. In scratch-js, though, you could (as far as I can tell) do something silly like create and render a new Costume
object every frame.
I can think of three ways to deal with this:
destroy
method to Costume
s that you require API consumers to call.Renderer
. Every rendering step, mark every texture that ends up getting rendered, then sweep away those that aren't.Sprite.costume
setter), update the refcount and delete the texture if the count is 0.I'm leaning heavily towards the second option myself--it seems like the least complex one.
The other implementation detail that might be affected is pen color, which is also being discussed in #22. While the canvas API accepts any CSS color string, WebGL requires colors in an RGBA format. That may factor into the pen color discussion somewhat, as every accepted pen color format will need to be converted into RGB(A).
I'd love to hear your thoughts on all of this--this is a really promising project!
So... let me just explain why I'd like to use Leapord;
I was trying to display my Scratch projects on my site, so I tried embeding them using Iframes, like so: <iframe src="https://scratch.mit.edu/projects/___/embed">
. But I encountered an issue; Scratch's cookies are applying to my site and i don't wat that.
I tried the HTML Sandbox attribute, but it results into Scratch "crashing". It could be because of Ajax requests being blocked because of it, since it works when I set the attribute's value to allow-same-origin (thus allowing Ajax requests, but allowing cookies with it).
Then I remembered that I could use Leapord, but of course, the projects (well, 2/3 of them) fail to convert. One was super large, and the other had a hacked block.
Although this isn't entirely Leapord related, can you help? Thanks.
And I couldn't contact you via Email, Dev or Twitter since my parents won't let me. :/
Would it be worthwhile to create some sort of chat server (like Discord or perhaps Matrix) where scratch-js and other Scratch-related development projects could be discussed? Sorry if this doesn't really fit here, but I'm not sure where else to put this--Scratch doesn't allow people to share usernames for other platforms and GitHub has no messaging feature.
Currently, the scratch-js Trigger.CLONE_START
triggers on the frame after "create clone" is called. In Scratch, it seems to be called immediately.
This manifests in the example project as clones appearing in their parent sprites' positions for one frame after they are created, which can be more easily seen by increasing their transparency:
Compare this to Scratch's behavior.
I'm better at HTML/CSS than I am at Javascript, so I'm considering starting a GUI for ScratchJS. I was wondering 1) if work on this should even start this early and 2) if it should be designed like an actual code editor or similar to Scratch. Feedback would be appreciated!
Scratch has some unexpected behaviours designed to make anything the programmer throws at it work.
Let's take the 'set costume' block as an example. Here's your implementation:
This would be the intuitive way of doing it. However, Scratch's algorithm is different:
number
takes priority
NaN
, Infinity
, -Infinity
are the first costumestring
takes priority if a costume with that name exists(Note that backdrops have a slightly different behaviour in 2.0, but in 3.0 there will be a breaking change to make them consistent with costumes, so we are allowed to not worry about this)
Who's responsibility is it to deal with all this unusual behaviour? Booleans have been used as costume names; so I don't think it's okay to ignore these intricacies.
In this case, because you can drop blocks in the slot, a compiler can't always know what the requested costume will be at runtime. However, it can do some stuff: for instance, it can know if a boolean will be returned by inspecting the block shape... except if you store a boolean in a variable/list (and note: type is preserved in these things, no matter how much Scratch tries to hide it from the programmer)
My conclusion, then, is that it has to be the library which replicates these quirks, because otherwise a compiler would have to add a bit of boilerplate each time, just in case... this would end up creating unintuitive JavaScript if the boilerplate has nothing to do with the code you're running (e.g. checking for a boolean when that could never happen).
So, if we want to be able to compile Scratch to JS, we will need to embed these quirks into our library (including the one I just referenced). Do you agree?
Currently, if you click a point on the stage where two sprites overlap, "when this sprite clicked" triggers will be started in both the sprites. For compatibility with Scratch, only the sprite layered on top should react to the click.
https://scratch.mit.edu/projects/435969858/
In this project the Stage stays blank
In the demo you just fall continuously, this seems to be because this.touching('Ground')
never returns to true, even after I disabled gravity and just moved into the black to the right, it still never returns true. This also happened regardless of whether or not fast collision is enabled.
After I replace the project ID with the one I'm interested in, the leopard stage stays black. If I press ENTER nothing happens. The project starts running only if I click outside the project URL box or if I press TAB. It would be better if the project starts as soon as the ID is pasted or the ENTER key is pressed.
When the green flag button is clicked it doesn't reset the sprite to where it should begin on the stage. A refresh is required in order to reset the project.
Currently, we're encouraging users to import scratch-js directly from github pages. This was fine for a while, but there isn't any version control. You're always importing the latest changes.
The best solution I can think of is to publish the package to npm and then encourage users to import using unpkg. In addition to loading specific versions, this also allows us to minify & bundle everything together so it loads faster.
Unfortunately, the name scratch-js is already taken on npm. (As is the domain scratchjs.com.) I'm planning to record a video explaining scratch-js very soon (while I have access to recording conditions), so it would be great to find a new name and publish within 24 hours.
Anybody have name ideas?
This might be a good time to add custom event listeners to Project
so we can do things like
project.on("green flag", () => {
// hide the ask box
});
from within the renderer.
I tried to translate my project https://scratch.mit.edu/projects/459257487/ and found some problems:
Right now pen color can be set to any valid CSS color string (like red
, #f00
, rgb(255, 0, 0)
, or others).
This isn't great because it doesn't leave much wiggle room for setting color, saturation, brightness, or transparency independently (which is possible in Scratch).
What's the best API for dealing with colors? Color strings are handy, but underpowered.
I have tested https://scratch.mit.edu/projects/417523534/ in Leopard. It seems a little faster in Leopard than in Scratch. But when you run it in Scratch in turbo mode, it is much faster. Is there a way to enable turbo mode in Leopard?
The current degree-normalisation algorithm
Has the potential to enter into an Infinite loop. The Scratch programming language exposes the value Infinity
. Observe that decrementing it by a finite value (like 360), does not change it's value. Therefore, the loop is infinite.
This does not match the behaviour of the Scratch programming language, and so intuitive in my opinion (especially since it can cause a crash in browsers).
Hence, I propose to use the native implementation of modulo arithmetic. This has the additional positive side-effect of making the code more optimisation and efficient.
The proposed code replacement is placed below:
((deg - 180) % 360) + 180
This is functionally equivalent to your current code for finite values. Both appear to be functionally equivalent to the Scratch 2.0 implementation.
Note that Scratch behaviour is to preserve current direction if a non-finite value is provided as a direction. Neither implementation is doing this, but they could with a simple if
statement. Newer versions of JavaScript have a Number.isFinite
(ES6, not supported in IE or Opera Mini). The codebase already uses ES6, so using this shouldn't be a problem and is perhaps the most readable solution.
Nice project btw!
Which is an issue, because I want to tell everyone that it's now possible to download a zip of projects translated on the scratch-js website!
This should have happened sooner, but I'm glad it's at least done now. :P
There's some broader discussion to be had about the ability to mess around with a project's sprites
after creating the project, and the best way to expose that. I talked about this a bit in a comment on another issue, but this is important because it touches the API surface and answers a lot of currently-open questions about various things in the codebase (e.g. loading projects and assets, layer ordering).
Right now, you can change which sprites appear in a project by adding to the project's sprites
object.
This means that if Leopard wants to run some of its own code when you add or remove a sprite (e.g. to add it to / remove it from an internal list of sprites in order to properly do layer ordering), it can't do that under the current API (in which you just add properties to the project.sprites
object) unless we decide to "fake" project.sprites
being an object by using a Proxy (which, as I mentioned earlier, cannot be polyfilled unlike the other ES6 features we use in this codebase, and seems like a janky way to implement things anyhow).
Scratch has its own internal model for how sprites/clones work: a "sprite" is the "prototype" object that holds the blocks, costumes, and sounds, and a "target" is a specific instance of a prototype "sprite" which holds things like transformation, current costume, and other state. The thing that differs from other languages is that you also always have a single instance of the sprite "prototype" on the stage, and clones are created from that.
We could require all sprite "prototypes" to be declared upfront (like in Scratch, where sprites can't create entire new sprites, only clones that share their own code/costumes/sounds); when you construct a Project
, you pass in all the Sprite
s that you will use in it, and then it will load all the costumes and sounds, let the renderer create WebGL textures, and do all the other prep work that needs to be done before starting the project. You can then only create additional sprites by cloning them.
We could also add a specific way to add or remove sprites-- a Project.addSprite
function, for example, could return a Promise
that resolves once Leopard is done doing things like loading the sprite's costumes. We could expose a similar API for dynamically adding costumes/sounds, if desired (currently, costumes just won't show up and sounds just won't play until they're loaded, which is weird).
There are likely other options; I just can't think of them at the moment. Right now, however, the API surface is super weird, blocks implementation of important features like layer ordering, and has "footgun" potential around the way assets (costumes + sounds) are loaded. It's important that we iron this out before too many Leopard projects end up relying on being able to modify a project's sprites
object.
In Scratch, the "touching (sprite)" block will return true if the current sprite is touching the other sprite or any of its clones. In scratch-js, it only returns true if it's touching the original sprite.
I think a good solution would be to retain the current behavior if you pass a sprite instance to touching()
, but to use Scratch's behavior if you pass the sprite's base class. I'd like to get your opinion on it.
Currently every sprite has the .stage
getter to access the stage instance. It should be equally easy to access other sprites in the project.
We could do something like this.sprite("sprite2")
, but accessing sprites by variable name doesn't seem ideal. Instead, we want to be passing around the actual instances where possible.
Another option would be for sprites to be stored in the Project
under an object. Something like this:
new Project({
sprites: {
sprite1: new Sprite1(/* ... */),
sprite2: new Sprite2(/* ... */)
},
stage: new Stage(/* ... */)
// ...
})
Then each sprite could have a getter that allows access to the sprites
object just like stage
. This means that inside a sprite you could do something like this.sprites.sprite2
.
Although Scratch doesn't have this, it would be nice to allow easy access to clones as well. Adding clones as top-level sprites in the sprites
object seems like a bad idea because it means that an id needs to be auto-generated for each clone that is created, which may be unpredictable.
Instead, each sprite can have an array called clones
with all the clones listed in order. Each clone can also keep track of its parent instance.
Are there any problems with this setup? Would love some feedback!
In Scratch, the two sprites can be touching even if they have ghost effect set to 100. Leopard does the less useful (but admittedly more reasonable) thing which is to treat the sprites as invisible and therefore not touching.
The real hero of the renderer is @adroitwhiz, but I can also take a stab at this if needed.
This issue was originally identified in project 2 here.
It seems like it doesn't happen with scratch, but it does with leopard. Here's the project link:
https://scratch.mit.edu/projects/524515171
Test it in scratch and in leopard. Let me know if there's a fix!
In project https://scratch.mit.edu/projects/305593150/ if you can click on the stage to activate the zoom feature. It seems that the click on the Stage is triggered even when you click on a sprite. For example, if I click the "reset" sprite, that is supposed to bring the view back to the starting point, what I get instead is a zoom, the click on the sprite is not taken into account (or at least the click on the Stage is triggered anyway, even if it should not)
this.timer
Custom blocks are a feature. There aren't any predefined blocks to support, because... well. You know.
Supporting hardware/api scratch extensions natively is out of the scope of Leopard.
Some triggers behave differently to being interrupted (triggered again whilst running) than others. We should investigate this and make sure scratch-js matches Scratch 3.0.
(Should update/add details to this issue eventually - just logging so that we remember to check this out later!)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.