+3 votes

I have a project that where I want the user to be able to save a screenshot in-game to their filesystem.

Here's how I generate the screenshot image:

get_viewport().set_clear_mode(Viewport.CLEAR_MODE_ONLY_NEXT_FRAME)
yield(VisualServer, "frame_post_draw")
var img = self.get_viewport().get_texture().get_data()
img.flip_y()
img.save_png("res://screenshot.png")

When the project is exported as a desktop app (tested on windows), this works just fine and saves the generated image to the project's directory.

However, in a web export, this doesn't work. Nothing is prompted for the user to save the image.

I tried a work around where it'd open the image in a new tab so the user could save it from there, using the below code:

var url = "data:image/png;base64," + Marshalls.raw_to_base64(img.data.data)
OS.shell_open(url)

But this doesn't work. Godot's OS.shell_open() doesn't seem to recognize data:image/png;base64, as a link to open using the web browser.

Is there anyway for me to save an image from a Godot project to the user's filesystem (either directly or by popping up a file-save prompt or by opening the image in a new tab) in a web exported build?

Godot version 3.3
in Engine by (18 points)

Try using this for Javascript downloads.

Oh, this is great! Thanks for the link

2 Answers

+2 votes
Best answer

Is there anyway for me to save an image from a Godot project to the user's filesystem (either directly or by popping up a file-save prompt or by opening the image in a new tab) in a web exported build?

Web browsers do not allow this for security reasons. You need to trigger a file download using JavaScript code (that can be called from GDScript) instead.

There is an experimental HTML5 Filesystem API that would allow websites to save files to arbitrary locations with the user's permission, but it's not widely supported and Godot doesn't implement it yet.

PS: Do not write files to res:// in an exported project, as the project directory should not assumed to be writable to. user:// should be used to save user data instead.

by (12,835 points)
selected by

Thanks! I remember reading about the user:// path but was just sticking with res:// in development.

Thanks for the clarification :)

+1 vote

Godot 3.4
1.Version: Accessing buffer directly from Image class

func Download_File(_img,_filename):
    var buf = _img.save_png_to_buffer()
    JavaScript.download_buffer(buf,_filename+".png")

2.Version: Accessing buffer from File class, as mentioned by Calinou here:
https://github.com/godotengine/godot/issues/27992

func Save_PNG(_img,_path,_filename):
    var _err = _img.save_png(_path+"/"+_filename+".png")
    Download_File(_path+"/"+_filename+".png",_filename)
    #Put Error Handling Here

func Download_File(_path,_filename):
    var f = File.new()
    f.open(_path,File.READ)
    var buf = f.get_buffer(f.get_len())
    JavaScript.download_buffer(buf,_filename+".png")
    f.close()
by (16 points)
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.