> ## Content Index
> Fetch the complete content index at: https://serpapi.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Desktop Wallpaper Changer with Node.js
- URL: https://serpapi.com/blog/wallpaper-changer/
- Published: 2023-02-23T20:05:09.000Z
- Updated: 2023-10-19T23:46:05.000Z
- Description: Programmatically change your desktop background to a randomized image from the web, with Node.js and SerpApi
- Author: Ryan Schafer
- Tags: NodeJS, JavaScript, serpapi, Web Scraping, Google Images, Automation

## Intro

Today we are going to build an application that will cycle your desktop background to different randomized images from the web, based on a theme and frequency you specify. I've included a level of detail in this tutorial that I hope is suitable for programmers who are new to Node.js.

If you do not need an explanation and just want to download and use the application, you can access the[ ](https://github.com/schaferyan/serpapi-google-search-rankings-nodejs)[](https://github.com/schaferyan/wallpaper-changer)[full code on GitHub](https://github.com/schaferyan/serpapi-google-search-rankings-nodejs).

## Prerequisites

- I can verify that this will work on Windows 10+ and Mac Ventura+, there seems to be a problem with the [wallpaper](https://www.npmjs.com/package/wallpaper) package on Linux systems.
- You will need to be somewhat familiar with JavaScript. You will have an easier time if you are familiar with ES6 syntax and features, as well as Node and Npm.
- You need to have the latest versions of Node and Npm installed on your system. If you don’t already have Node and Npm installed you can visit the following link for help with this process: <https://docs.npmjs.com/downloading-and-installing-node-js-and-npm>
- You need to have an IDE installed on your system that supports JavaScript and Node.js. I recommend VSCode or Sublime, but any IDE that supports Node.js will do.
- You will also need to sign up for a free SerpApi account at <https://serpapi.com/users/sign%5Fup>.

## Preparation

First you will need to create a **package.json** file. Open a terminal window, create a directory for the project, and CD into the directory.

```bash
mkdir wallpaper-changer
cd wallpaper-changer

```

Create your **package.json** file:

```bash
npm init

```

Npm will walk you through the process of creating a **package.json** file.  
After this we need to make a few changes to the **package.json** file.  
We will be using the ES Module system rather than CommonJs, so you need to add the line `type="module"` to your package.json.

```json
{
  "name": "wallpaper-changer",
  "version": "1.0.0",
  "description": "Change your wallpaper to a randomized image from the web",
  "type": "module",
  "main": "index.js",

```

We will also add a start script for convenience:

```json
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

```

Next we need to install our dependencies. For this project we will use the [new SerpApi for JavaScript/TypeScript Node module](https://www.npmjs.com/package/serpapi). We will also use the [image-downloader](https://www.npmjs.com/package/image-downloader), [wallpaper](https://www.npmjs.com/package/wallpaper), [write-json-file](https://www.npmjs.com/package/write-json-file), [node-schedule](https://www.npmjs.com/package/node-schedule), and [dotenv](https://www.npmjs.com/package/dotenv) modules. Most of these have self explanatory names. If you're not familiar with [dotenv](https://www.npmjs.com/package/dotenv), it is a module for handling environment variables (like file paths and API keys). 

```bash
npm install serpapi dotenv wallpaper node-schedule write-json-file image-downloader

```

If you haven’t already signed up for a free Serpapi account go ahead and do that now by visiting <https://serpapi.com/users/sign%5Fup> and completing the signup process.

Once you have signed up, verified your email, and selected a plan, navigate to <https://serpapi.com/manage-api-key> . Click the Clipboard icon to copy your API key.

Then create a new file in your project directory called **.env** and add the following line:

```
SERPAPI_KEY = “PASTE_YOUR_API_KEY_HERE”

```

You will also want to specify the absolute path to a folder where you want to store the images you download:

```
PATH_TO_IMG = “ABSOLUTE_PATH_TO_DIRECTORY”

```

Create a file called **index.js** and add the following:

```javascript
import { config } from "serpapi";
import * as dotenv from "dotenv";

dotenv.config();
config.api_key = process.env.SERPAPI_KEY;

```

## Scraping Google Images with SerpApi

We want to use SerpApi to search Google Images, and return a list of URLS we can download images from.

First we will need to determine our search parameters. We will want as much as possible to limit our search to only images with resolution and dimensions suitable for desktop wallpaper:

![](https://storage.ghost.io/c/a5/00/a5004977-0dd2-4bcd-9292-dd0e05d4c59e/content/images/2023/02/image-2.png)

You can then further filter the images by selecting one or more "chips". You can see here I have selected nature, but you can select anything you like.

![](https://storage.ghost.io/c/a5/00/a5004977-0dd2-4bcd-9292-dd0e05d4c59e/content/images/2023/02/image-5.png)

In order to use these filters or "chips" in our request to SerpApi, we will need to get the value of the `chips` parameter in the URL:

![](https://storage.ghost.io/c/a5/00/a5004977-0dd2-4bcd-9292-dd0e05d4c59e/content/images/2023/02/image-6.png)

We want everything after "chips=" and before the "&" sign.

These are just suggestions. You can use any query you like, and skip "chips" if you don't need them to get a selection you like. For example instead of "4k desktop wallpaper" you can try searching for "desktop wallpaper nature" or even just "nature photos", or anything you like. Keep in mind that any image you see in the first page of results could be randomly selected as your desktop.

Now that we have selected the image search results page we want to use, we are ready to write the code to scrape it. For this we will use [SerpApi](https://serpapi.com/). SerpApi is a web-scraping API that streamlines the process of scraping data from search engines. This can also be done manually, but SerpApi provides several distinct advantages. Besides providing real time data in a convenient JSON format, SerpApi covers proxies, captcha solvers and other necessary techniques for avoiding searches being blocked. You can[ ](https://serpapi.com/blog/web-scraping-google-images-with-nodejs/)[click here](https://dev.to/mikhailzub/how-to-scrape-google-search-organic-results-with-node-js-4id9) for an in depth tutorial to scrape Google Images both manually and with SerpApi, or [here](https://serpapi.com/blog/how-to-reduce-chance-of-being-blocked-while-web/) for an overview of the techniques you can use to prevent getting blocked when scraping the web.

 Create a new file and name it **image-search.js**. Then add the following:

```javascript
import { getJson } from "serpapi";

export async function getImageResults(q, chips) {
  // The parameters we will include in the the GET request
  const params = {
    q: q,
    location: "Austin, Texas, United States", //your location
    google_domain: "google.com", //the google domain for your location
    gl: "us", //your two letter country code
    hl: "en", //language
    chips: chips,
    tbm: "isch", //specifies that this an image search
  };

  // call the API and wait for it to return the data
  const data = await getJson("google", params);
  return data;
}
```

If you want to use the same images that came up in your browser, be sure to replace the `location`, `google_domain`, `gl`, and `hl` parameters with the correct values for your location. We are using method parameters to set `q` and `chips` so that these will be easy to change later if we decide we want a different image selection.

Now in your **index.js** file, import the method we just defined and call it with the `q` and `chips` parameters you chose for your image search. 

**index.js** should now look like this:

```javascript
import { config } from "serpapi";
import * as dotenv from "dotenv";
import { getImageResults } from "./image-search.js";

dotenv.config();
config.api_key = process.env.SERPAPI_KEY;

const results = await getImageResults("4k desktop wallpaper", "q:4k+desktop+wallpaper,g_1:nature:Vy6qxRTDEsA%3D");

console.log(results);
```

You should see something like this in your console:

![](https://storage.ghost.io/c/a5/00/a5004977-0dd2-4bcd-9292-dd0e05d4c59e/content/images/2023/02/image-9.png)

If you scroll down in your terminal to where it says `image_results` you will see that each image has the `title`, `link`, and `original` fields. The `link` will take you to the website where the image was found. `original` is the URL you can use to download the image. We are going to scrape `title` and `original` in this tutorial.

![](https://storage.ghost.io/c/a5/00/a5004977-0dd2-4bcd-9292-dd0e05d4c59e/content/images/2023/02/image-10.png)

We are going to write a method now that will extract these fields from each image in the `image_results`, and return an array of objects.

Add the following code to your `image_search.js` file:

```javascript
export async function getImageUrls(q, chips) {
  // perform the search and store the results in a variable
  const results = await getImageResults(q, chips);

  // extract the image urls and titles and store them in an array of Objects
  const urls = results.images_results.map((img) => {
    return { link: img.original, title: img.title };
  });
  return urls;
}
```

Then return to your **index.js** file and import the new method:

```javascript
import { getImageUrls } from "./image-search.js";
```

Modify the code you used to print the search results, so that it prints the image titles and URLs instead:

```javascript
const results = await getImageUrls("4k desktop wallpaper", "q:4k+desktop+wallpaper,g_1:nature:Vy6qxRTDEsA%3D");
console.log(results);
```

Check your terminal output and make sure are printing a list of urls and titles.

## Downloading the Image

Now that we have URLs for some images, we want to select a random one and download it. In **index.js,** comment out your `console.log` statement for now, and add the code to select a random URL from the list:

```javascript
const urls = await getImageUrls("4k desktop wallpaper", "q:4k+desktop+wallpaper,g_1:nature:Vy6qxRTDEsA%3D");
// console.log(results);
const randInt = Math.floor(Math.random() * urls.length);
const randImgUrl = urls[randInt]["link"];
```

Create a new file called **image-downloader.js**. The code should look like this:

```javascript
import * as download from "image-downloader";

export async function downloadImg(url) {
  //specify a url and the path to where you want to store the image
  const options = {
    url,
    dest: process.env.PATH_TO_IMG,
  };

  let img;

  // download the image
  await download
    .image(options)
    .then(({ filename }) => {
      console.log("Saved to", filename);
      img = filename.toString();
    })
    .catch((err) => console.error(err));

  // return the filename so we can find the image later
  return img;
}
```

Here we are accepting a URL as a parameter, and attempting to download an image from that URl into the directory we specified in our **.env** file. We return the file name so that we can find it when we want to set our wallpaper.

Go back to **index.js.** Import the `downloadImg()` function, and call it passing the randomly select URL as the parameter:

```javascript
import { downloadImg } from "./image-downloader.js";

const urls = await getImageUrls("4k desktop wallpaper", "q:4k+desktop+wallpaper,g_1:nature:Vy6qxRTDEsA%3D");
// console.log(results);
const randInt = Math.floor(Math.random() * urls.length);
const randImgUrl = urls[randInt]["link"];
downloadImg(randImgUrl);
```

If you run this code, then check the directory you specified for image downloads in your **.env** file, you should see an image.

## Setting the Desktop Wallpaper Programmatically

Create a new file and name it **wallpaper-changer.js**. Import the functions you wrote for scraping URLs and downloading images, as well as the [wallpaper](https://www.npmjs.com/package/wallpaper) module:

```javascript
import { getImageUrls } from "./image-search.js";
import { downloadImg } from "./image-downloader.js";
import { getWallpaper, setWallpaper } from "wallpaper";
```

Create an async function `changePaper()` inside **wallpaper-changer.js.** Copy the code you wrote in **index.js** in the previous step for selecting and downloading a random image and paste it inside the `changePaper()` function. Instead of hardcoding `q` and `chips` though, lets include them as method parameters:

```javascript
export async function changePaper(q, chips) {
  const urls = await getImageUrls(q, chips);
  
    // select a random url from the array
  const randInt = Math.floor(Math.random() * urls.length);
  const randImgUrl = urls[randInt]["link"];
 
    // download the image
  const img = await downloadImg(randImgUrl);

  // set desktop wallpaper
  try{
    await setWallpaper(img);
    console.log("Set wallpaper to: " + urls[randInt]["title"]);
  }catch(e){
    console.log("e");
    console.log("current wallpaper is " + wallpaper);
  }
}
```

Then go back to **index.js** and import the `changePaper()` method. Replace the code you just copied and pasted with a call to `changePaper()`, passing in your desired `q` and `chips` values:

```javascript
import { config } from "serpapi";
import * as dotenv from "dotenv";
import { changePaper } from "./wallpaper-changer.js";

dotenv.config();
config.api_key = process.env.SERPAPI_KEY;

changePaper(
  "4k desktop wallpaper",
  "q:4k+desktop+wallpaper,g_1:nature:Vy6qxRTDEsA%3D"
);
```

If you run `npm start` now, you should see your wallpaper change!

## Scheduling the Wallpaper Change as a Recurring Task

Now you can download a random image from the web as your wallpaper. But what if you want to do this every Friday at 10 am? Or what if you want a new image every minute, like a slideshow?

The most common way to schedule repeating tasks as a background process is to use something called [Cron Jobs](https://www.hostinger.com/tutorials/cron-job). But in order to keep things simple, we are sticking to all JavaScript in this tutorial.

We can accomplish this with JavaScript using [node-schedule](https://www.npmjs.com/package/node-schedule).

First create a new file, name it **scheduler.js** and add the following code:

```javascript
import * as schedule from "node-schedule"

export async function scheduleJob(callback, time){

    console.log("scheduling wallpaper change");
    
    // create a RecurrenceRule Object to define when you want the job to repeat
    const rule = new schedule.RecurrenceRule(); 
    
    // copy the properties from the time object to the RecurrenceRule instance
    Object.assign(rule, time); 
    
    // schedule the job
    schedule.scheduleJob(rule, callback); 
}
```

Notice the two parameters for the `scheduleJob()` function - `callback` and `time`. 

`callback` is the function we want to schedule to run, and `time` is an object with properties specifying when we want the function to run.

Go back now to your **index.js**. We don't want to call our `changePaper()` function right away anymore, instead we want to pass it in as the first argument to the `scheduleJob()` function. We also need to define a `time` object to specify when we want the code to run. Don't forget to add the import statement for the `schedule Job` function:

```javascript
import { config } from "serpapi";
import * as dotenv from "dotenv";
import { changePaper } from "./wallpaper-changer.js";
import { scheduleJob } from "./scheduler.js";

dotenv.config();
config.api_key = process.env.SERPAPI_KEY;

const callback = () =>
  changePaper(
    "4k desktop wallpaper",
    "chips=q:desktop+wallpaper+4k,g_1:nature:uPhOf0Whosk%3D"
  );

const time = {
  second: 0,
  //hour: 0,
  //minute: 0,
  //dayOfWeek: 0,
  //tz: "PST"
};

scheduleJob(callback, time);
```

The code above will cause the wallpaper to change once every minute. You can change the values in the time variable to anything you want. I suggest starting with every minute through, to make sure it is working. 

You can use any of the following key-value pairs in your time object:

- `second (0-59)`
- `minute (0-59)`
- `hour (0-23)`
- `date (1-31)`
- `month (0-11)`
- `year`
- `dayOfWeek (0-6) Starting with Sunday`
- `tz`

You can read more about how to specify different times at <https://www.npmjs.com/package/node-schedule>.

Now you can run `npm start` and watch what happens.

## Saving the URLS to a file

You have successfully built an application that periodically downloads a random image from the web and sets it as your desktop wallpaper. You may have noticed there is a part of this process that is incredibly inefficient - we are scraping the image URLS again each time we change the image. It costs a search credit every time we call SerpApi (unless it is an identical call within the hour, in which case the results are fetched from the cache). In any case we want to avoid making unnecessary repeat API calls.

To solve this we will use the [write-json-file](https://www.npmjs.com/package/write-json-file) node module to save the URLs in an external JSON file, instead of holding them in memory and repeating our request over and over again. We will also use the `fs` module here for interacting with the file system. Node.js ships with `fs` , so you don't need to install it, but we do need to add import statements. 

Open your **wallpaper-changer.js** file again. The code here is a bit more convoluted. We first have to check whether the ".json" file already exists. If it exists we want to read it.

If the file doesn't exist, or if it is empty, we want to call our the `getImageUrls()` function to request search results and extract the URLS, then we want to create the ".json" file and write the URLs to it.

```javascript
import { getImageUrls } from "./image-search.js";
import { downloadImg } from "./image-downloader.js";
import { getWallpaper, setWallpaper } from "wallpaper";
import { writeJsonFile } from "write-json-file";
import { readFile } from "fs/promises";
import { existsSync } from "fs";

export async function changePaper(q, chips) {
  let urls;

  // check whether the file where we save the urls exists
  const fileExists = existsSync("./image-urls.json");

  // if the file exists, parse the data from it into the "urls" variable
  if (fileExists) {
    const json = JSON.parse(
      await readFile(new URL("./image-urls.json", import.meta.url))
    );
    urls = json.urls;
  }

  // If the file doesn't exist or is empty, download the image urls and create/write the urls to the file
  if (!fileExists | !urls) {
    urls = await getImageUrls(q, chips); // search google images and extract the image urls
    await writeJsonFile("image-urls.json", { urls });
  }

  // select a random url from the array
  const randInt = Math.floor(Math.random() * urls.length);
  const randImgUrl = urls[randInt]["link"];

  // download the image
  const img = await downloadImg(randImgUrl);

  // set desktop wallpaper
  try{
    await setWallpaper(img);
    console.log("Set wallpaper to: " + urls[randInt]["title"]);
  }catch(e){
    console.log("couldn't set wallpaper");
    console.log("e");
  }
  // verify that we've changed the wallpaper, useful for troubleshooting
  const wallpaper = await getWallpaper();
  console.log("current wallpaper is " + wallpaper);
}
```

## Conclusion

We have created a Node.js application to make our desktop wallpaper cycle through random images from the web at scheduled intervals. You now have a template you can modify in any way you like. You can try out different themes by modifying the values of `q` and `chips`, and deleting the .json file. 

It may have occurred to you that if you keep downloading images indefinitely, eventually they will take up too much space on your hard drive. 

Ideally we would want to add code to check how many images are in the directory, and delete them after a specified limit. Or delete them every time we change the background, or after every time the program runs. I haven't covered this here but if you want to implement it, this blog post post explains how to delete files using `fs`: [https://www.bezkoder.com/node-js-delete-file/#:\~:text=To delete a file in,loop because it works asynchronously](https://www.bezkoder.com/node-js-delete-file/#:~:text=To%20delete%20a%20file%20in,loop%20because%20it%20works%20asynchronously).

You will also have to keep the terminal window open for the program to run and continue changing your desktop background at scheduled intervals. If you want it to run as a background process, I suggest learning about [Cron Jobs](https://www.hostinger.com/tutorials/cron-job) and replacing the [node-schedule](https://www.npmjs.com/package/node-schedule) module and our **scheduler.js** file with a Cron Job.

I hope you found the tutorial informative and easy to follow. If you have any questions, feel free to contact me at ryan@serpapi.com.

## Links

### SerpApi:

- [Google Images API](https://serpapi.com/images-results)
- [SerpApi Playground](https://serpapi.com/playground?q=4k+desktop+images+space&location=Austin%2C+Texas%2C+United+States&gl=us&hl=en&tbm=isch&google%5Fdomain=google.com&newPara=stick+highlight+lr+as%5Fqdr+safe+google%5Fdomain+si+form+filter+chips+client)
- [SerpApi JavaScript/TypeScript Library](https://www.npmjs.com/package/serpapi)

### Node modules:

- [image-downloader](https://www.npmjs.com/package/image-downloader)
- [wallpaper](https://www.npmjs.com/package/wallpaper)
- [write-json-file](https://www.npmjs.com/package/write-json-file)
- [node-schedule](https://www.npmjs.com/package/node-schedule)
- [dotenv](https://www.npmjs.com/package/dotenv)
- [serpapi](https://www.npmjs.com/package/serpapi)