{"id":22647730,"url":"https://github.com/davidhintelmann/go-screenshot","last_synced_at":"2025-07-28T19:08:41.235Z","repository":{"id":189569305,"uuid":"680892098","full_name":"davidhintelmann/go-screenshot","owner":"davidhintelmann","description":"Take screenshots with Go Programming Language","archived":false,"fork":false,"pushed_at":"2023-08-20T18:47:17.000Z","size":7,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T06:48:13.754Z","etag":null,"topics":["go","golang","screenshot"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidhintelmann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-08-20T18:34:01.000Z","updated_at":"2025-01-06T10:46:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"11dba988-ce34-4bc3-a030-1a41705a7378","html_url":"https://github.com/davidhintelmann/go-screenshot","commit_stats":null,"previous_names":["davidhintelmann/go-screenshot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/davidhintelmann/go-screenshot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidhintelmann%2Fgo-screenshot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidhintelmann%2Fgo-screenshot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidhintelmann%2Fgo-screenshot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidhintelmann%2Fgo-screenshot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidhintelmann","download_url":"https://codeload.github.com/davidhintelmann/go-screenshot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidhintelmann%2Fgo-screenshot/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267569706,"owners_count":24109128,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","screenshot"],"created_at":"2024-12-09T07:34:23.931Z","updated_at":"2025-07-28T19:08:41.205Z","avatar_url":"https://github.com/davidhintelmann.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Introduction\r\n\r\nI was looking for different ways to automate capturing a screenshot of my desktop.\r\nIt would be possible to use python but using go may be faster since it is a compiled language.\r\nThen I will need to figure out how to capture portions of my screen instead of the entire thing incase I want to avoid capturing any timestamp from the operating system's clock.\r\n\r\n## Environment\r\n\r\n**Language:** [Go](https://go.dev) version 1.21\r\n\r\n**Operating System:** Windows 11 (developed) - Linux Ubuntu (tested)\r\n\r\n## kbinani's repo for taking screenshots\r\n\r\nIn this exercise we will be importing [kbinani's](https://github.com/kbinani/screenshot) screenshot library to capture desktop screens to a `.png` image.\r\n\r\nBrief overview of this libraries features:\r\n\u003e - Go library to capture desktop screen.\r\n\u003e - Multiple display supported.\r\n\u003e - Supported GOOS: windows, darwin, linux, freebsd, openbsd, and netbsd.\r\n\u003e - cgo free except for GOOS=darwin.\r\n\r\nIn this blog post I will be discussing the first two points above, since I am developing this program on Windows 11. I have tested capturing a few screenshots on Linux Ubuntu but I haven't modified the code to detect the OS and crop the image's timestamp out. This feature currently only works on Windows 11.\r\n\r\nkbinani's library is relatively simple since there are only five functions:\r\n\r\n* Capture\r\n  * Capture returns screen capture of specified desktop region. x and y represent distance from the upper-left corner of primary display. Y-axis is downward direction. This means coordinates system is similar to Windows OS.\r\n* CaptureDisplay\r\n  * CaptureDisplay captures whole region of displayIndex'th display, starts at 0 for primary display.\r\n* CaptureRect\r\n  * CaptureRect captures specified region of desktop.\r\n* GetDisplayBounds\r\n  * GetDisplayBounds returns the bounds of displayIndex'th display. The main display is displayIndex = 0.\r\n* NumActiveDisplays\r\n  * NumActiveDisplays returns the number of active displays.\r\n\r\nMore information about this librarie's functions at its [pkg.go.dev](https://pkg.go.dev/github.com/kbinani/screenshot#pkg-functions) repo.\r\n\r\n## Explanation of Code\r\n\r\nBefore diving into the code I will give a brief overview on the folder structure for this project. As this is a simple program, we also have a simple folder structure.\r\n\r\n### Working Directory\r\n\r\nYour working directory, called screenshot, should look like:\r\n - screenshot\r\n   - main.go\r\n   - go.mod\r\n   - go.sum\r\n - img\r\n\r\nimg directory does not need to be created, this will be done automatically. The file `main.go` is where all our go code will be since this is a simple program. The other two files `go.mod` may need to be cleaned up using the `go mod tidy` command in the working directory to manage any dependencies, and `go.sum` file is used to validate the checksum of each direct and indirect dependency to confirm that none of them have been modified.\r\n\r\n### Imports and Package Name\r\n\r\nBelow we name the package `package main` and after this import all the necessary packages for this code to run correctly.\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"image\"\r\n\t\"image/png\"\r\n\t\"log\"\r\n\t\"os\"\r\n\t\"strings\"\r\n\t\"time\"\r\n\r\n\t\"github.com/kbinani/screenshot\"\r\n)\r\n```\r\n\r\n### Folder Name for Screenshots\r\n\r\nAs shown in the [Working Directory](#working-directory) section, our folder name for saving images is called `img` which is declared below.\r\n\r\n```go\r\n// folder name in this directory\r\n// where screenshots will be saved\r\nconst folderName string = \"img/\"\r\n```\r\n\r\n### Main Function\r\n\r\nThe main function for each go program is where the program begins. First we use the `NumActiveDisplays()` function from kbinani's library to return the number of displays so we can loop through all of them and take a screenshot. Once we start looping over all the displays available we use the `GetDisplayBounds()` function to find the pixel size of the current display that we are looping over.\r\n\r\n```go\r\nfunc main() {\r\n\tn := screenshot.NumActiveDisplays()\r\n\r\n\tfor i := 0; i \u003c n; i++ {\r\n\t\t// get entire screen's bounds by pixel WxH\r\n\t\tbounds := screenshot.GetDisplayBounds(i)\r\n        // ...\r\n    }\r\n}\r\n```\r\n\r\n### Check if img Folder Exists\r\n\r\nNext in the main function, we check if the directory exists which we declared above as `folderName`. If it does not exist it will be created.\r\n\r\n```go\r\n\t\t// check folderName folder\r\n\t\t// check if directory already exists\r\n\t\t// else create it\r\n\t\tfolderInfo, err := os.Stat(folderName)\r\n\t\tif os.IsNotExist(err) {\r\n\t\t\te := os.Mkdir(folderName, 0755)\r\n\t\t\tif e != nil {\r\n\t\t\t\tlog.Fatal(\"Could not create img folder where screenshots are saved.\")\r\n\t\t\t}\r\n\t\t}\r\n\t\tlog.Println(folderInfo)\r\n```\r\n\r\n### Create File\r\n\r\nNow we will create a file in the `folderName` folder to save the `.png` data to. This file name will be based on the display number followed by the date and time. The format is:\r\n\r\n    d_YYYY-MM-DD_XXHXXMXXS.png\r\n\r\n    where:\r\n        d - display number in int\r\n        YYYY - year\r\n        MM - month\r\n        DD - day\r\n        XXH - hours\r\n        XXM - minutes\r\n        XXS - seconds\r\n    \r\n    example: 0_2023-08-20_06H29M12S.png\r\n        display - 0\r\n        year - 2023\r\n        MM - 08 (August)\r\n        DD - 20\r\n        XXH - 6 AM\r\n        XXM - 29 minutes\r\n        XXS - 12 seconds\r\n\r\ngo code\r\n\r\n```go\r\n        // create file before saving png\r\n\t\t// convert file name to:\r\n\t\t// d_YYYY-MM-DD_XXHXXMXXS.png\r\n\t\t// where\r\n\t\t// d - display number in int\r\n\t\t// YYYY - year\r\n\t\t// MM - month\r\n\t\t// DD - day\r\n\t\t// XXH - hours\r\n\t\t// XXM - minutes\r\n\t\t// XXS - seconds\r\n\t\tnow := time.Now()\r\n\t\tnowString := now.String()[:19]\r\n\t\tnowString = strings.Replace(nowString, \" \", \"_\", 1)\r\n\t\tnowString = strings.Replace(nowString, \":\", \"H\", 1)\r\n\t\tnowString = strings.Replace(nowString, \":\", \"M\", 1)\r\n\t\tnowString += \"S\"\r\n\t\tfilePath := folderName\r\n\t\tfileName := fmt.Sprintf(filePath+\"%d_%s.png\", i, nowString)\r\n\t\tfile, _ := os.Create(fileName)\r\n\t\tdefer file.Close()\r\n```\r\n\r\n### Take Screenshot\r\n\r\nNow we will finally take a screenshot with the `screenShot()` that I created which takes two parameters. The first parameter is of type `bool` and is either `true` or `false`. If set to `false` then the screenshot will not include the timestamp of Windows 11 operating system in the bottom right corner. If the first parameter is set to `true` then the timestamp will be included in the screenshot, i.e. the entire screen will be captured. The second parameters passes the bounds of the screen to the function.\r\n\r\n{{\u003c alert \u003e}}\r\n**Warning!** if you are running this code on Linux it should work (worked on my Ubuntu set up) but remember to set the first parameter to true since the operating system's clock location is different for Linux and Windows. The screenshot function will have to modified if you plan on using this code on anything other than Windows 11.\r\n{{\u003c /alert \u003e}}\r\n\r\nAfter the screenshot is taken it will be saved as a `.png`\r\n\r\n```go\r\n        // take the screenshot\r\n\t\timg, err := screenShot(false, bounds)\r\n\t\tif err != nil {\r\n\t\t\tlog.Fatalln(\"Could not take a screenshot.\")\r\n\t\t}\r\n\r\n\t\t// save image\r\n\t\tpng.Encode(file, img)\r\n```\r\n\r\n### screenShot Function\r\n\r\nI created a function which is described above in the [Take Screenshot](#take-screenshot) section.\r\n\r\n```go\r\nfunc screenShot(timestamp bool, captureBox image.Rectangle) (*image.RGBA, error) {\r\n\tif timestamp {\r\n\t\timg, err := screenshot.CaptureRect(captureBox)\r\n\t\tif err != nil {\r\n\t\t\tpanic(err)\r\n\t\t}\r\n\t\treturn img, err\r\n\t} else {\r\n\t\t// create newBounds for screenshot\r\n\t\t// reduce number of y pixels to remove\r\n\t\t// operating system clock\r\n\t\t// subtract 50 from height on Windows 11\r\n\t\tnewBounds := image.Rectangle{\r\n\t\t\timage.Point{0, 0}, // minimum pixels\r\n\t\t\timage.Point{captureBox.Dx(), captureBox.Dy() - 50}, // maximum pixels\r\n\t\t}\r\n\t\timg, err := screenshot.CaptureRect(newBounds)\r\n\t\tif err != nil {\r\n\t\t\tpanic(err)\r\n\t\t}\r\n\t\treturn img, err\r\n\t}\r\n}\r\n```\r\n\r\n### Install and run this code\r\n\r\nIn order to run this code you will have two options. It is also assumed you have go version 1.21 installed.\r\n\r\n1. Create a new file called `main.go` and paste in the code from the [Full Code](#full-code) section below. \r\n   - Navigate to the directory, in terminal, where your `main.go` file is\r\n   - Run the commands:\r\n\t\u003e go mod init\r\n\r\n\t\u003e go mod tidy\r\n   - After you have initialized and tidied up the `go.mod` file you can run:\r\n\t\u003e go run main.go\r\n   - This will execute the program and take your screenshot!\r\n2. Another method would be to clone my [repo](https://github.com/davidhintelmann/go-screenshot)\r\n\t\u003e git clone https://github.com/davidhintelmann/go-screenshot.git\r\n   - Navigate to the directory, in terminal, where your `main.go` file is\r\n   - Run the command:\r\n\t\u003e go mod tidy\r\n   - After you have tidied up the `go.mod` file you can run:\r\n\t\u003e go run main.go\r\n   - This will execute the program and take your screenshot!\r\n\r\nYou will find your screenshot in the `img` folder.\r\n\r\n## Full Code\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"image\"\r\n\t\"image/png\"\r\n\t\"log\"\r\n\t\"os\"\r\n\t\"strings\"\r\n\t\"time\"\r\n\r\n\t\"github.com/kbinani/screenshot\"\r\n)\r\n\r\n// folder name in this directory\r\n// where screenshots will be saved\r\nconst folderName string = \"img/\"\r\n\r\nfunc main() {\r\n\tn := screenshot.NumActiveDisplays()\r\n\r\n\tfor i := 0; i \u003c n; i++ {\r\n\t\t// get entire screen's bounds by pixel WxH\r\n\t\tbounds := screenshot.GetDisplayBounds(i)\r\n\r\n\t\t// check folderName folder\r\n\t\t// check if directory already exists\r\n\t\t// else create it\r\n\t\tfolderInfo, err := os.Stat(folderName)\r\n\t\tif os.IsNotExist(err) {\r\n\t\t\te := os.Mkdir(folderName, 0755)\r\n\t\t\tif e != nil {\r\n\t\t\t\tlog.Fatal(\"Could not create img folder where screenshots are saved.\")\r\n\t\t\t}\r\n\t\t}\r\n\t\tlog.Println(folderInfo)\r\n\r\n\t\t// create file before saving png\r\n\t\t// convert file name to:\r\n\t\t// d_YYYY-MM-DD_XXHXXMXXS.png\r\n\t\t// where\r\n\t\t// d - display number in int\r\n\t\t// YYYY - year\r\n\t\t// MM - month\r\n\t\t// DD - day\r\n\t\t// XXH - hours\r\n\t\t// XXM - minutes\r\n\t\t// XXS - seconds\r\n\t\tnow := time.Now()\r\n\t\tnowString := now.String()[:19]\r\n\t\tnowString = strings.Replace(nowString, \" \", \"_\", 1)\r\n\t\tnowString = strings.Replace(nowString, \":\", \"H\", 1)\r\n\t\tnowString = strings.Replace(nowString, \":\", \"M\", 1)\r\n\t\tnowString += \"S\"\r\n\t\tfilePath := folderName\r\n\t\tfileName := fmt.Sprintf(filePath+\"%d_%s.png\", i, nowString)\r\n\t\tfile, _ := os.Create(fileName)\r\n\t\tdefer file.Close()\r\n\r\n        // take the screenshot\r\n\t\timg, err := screenShot(false, bounds)\r\n\t\tif err != nil {\r\n\t\t\tlog.Fatalln(\"Could not take a screenshot.\")\r\n\t\t}\r\n\r\n\t\t// save image\r\n\t\tpng.Encode(file, img)\r\n\t}\r\n}\r\n\r\nfunc screenShot(timestamp bool, captureBox image.Rectangle) (*image.RGBA, error) {\r\n\tif timestamp {\r\n\t\timg, err := screenshot.CaptureRect(captureBox)\r\n\t\tif err != nil {\r\n\t\t\tpanic(err)\r\n\t\t}\r\n\t\treturn img, err\r\n\t} else {\r\n\t\t// create newBounds for screenshot\r\n\t\t// reduce number of y pixels to remove\r\n\t\t// operating system clock\r\n\t\t// subtract 50 from height on Windows 11\r\n\t\tnewBounds := image.Rectangle{\r\n\t\t\timage.Point{0, 0}, // minimum pixels\r\n\t\t\timage.Point{captureBox.Dx(), captureBox.Dy() - 50}, // maximum pixels\r\n\t\t}\r\n\t\timg, err := screenshot.CaptureRect(newBounds)\r\n\t\tif err != nil {\r\n\t\t\tpanic(err)\r\n\t\t}\r\n\t\treturn img, err\r\n\t}\r\n}\r\n\r\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidhintelmann%2Fgo-screenshot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidhintelmann%2Fgo-screenshot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidhintelmann%2Fgo-screenshot/lists"}