text:
Display the Hacker Logo
You may have heard of the "Hacker Logo", also called the "Hacker Emblem". It looks like this:

This is a pattern from a mathematical simulation called the Game of Life. The glider is the simplest Life pattern that moves, and the most instantly recognizable of all Life patterns.
The challenge
The challenge is pretty simple: Display the hacker logo. This is defined as:

A 3x3 grid with a border, a white background and gray gridlines.
Five black dots arranged in the GoL glider pattern.
Nothing else.

The rules

The black dots must fill 40%-80% of their individual grid-boxes.
You will display the emblem with graphical output but no ASCII art.
The output must be at least 30x30 pixels.
The output must only have the colors gray, black and white.
Each grid-box in the grid will be the same size. The grid will be a regular 3x3 square.
You may not pull the logo from the internet or your filesystem.
Your program will display the logo on an empty screen/window. If it terminates it must do so normally.
Note that "dots" does not necessarily mean "circles". A "dot" is a single geometric shape centered in the middle of the grid-box with one surface. For example, while a circle or square will qualify as a dot, two triangles or a checkerboard will not.

The winner
As this is code-golf, the shortest answer in each language wins!
Please include a screenshot of the output of your program in your answer.

I had no idea this was called the Hacker Logo. I have used it as my avatar on some sites, guess that makes me a Hacker.

@MarkThomas that or a GoL nerd xD

Is it acceptable to use black gridlines?

@Octopus gray gridlines. Nope, sorry. That would make it a bit too easy.

What does "clear the screen" mean? Is it okay if it displays it in a window?

@DavidConrad "clear the screen" means if you're using an IDE/interface that has builtin graphics you can't display it with existing text on the screen. Yes, you can display it in a window.

Is multiple shades of gray (and related off colors) due to anti-aliasing allowed?

Do we have to display the output or can we return it or save to a file?

The last sentence of the rules is a bit unclear. Is it supposed to say that triangles or checkerboards will not qualify?

Should the borders be single-lined? See most HTML solutions that use tables below.

@Gieron Oops, I missed that from a previous revision of the post. Thanks.

■◙■\n■■◙\n◙◙◙ is a no-go :P?

Could you clarify if "The black dots must fill 40%-80% of their individual grid-boxes" means: (1) that 40-80% of the pixels of a box's white fill should become black by drawing a dot; or (2) that the width of the dot should be 40-80% of the width of the white fill? (a 40% area dot is deceptively big).

@JonathanAllan The first one.

@IanMiller Yep, by "gray" I didn't mean any specific color, I meant "generic gray".

@TheLethalCoder Display the hacker logo

@carusocomputing Nope :P

@ErnodeWeerd Could you clarify what you mean by "single-lined"?

@MarkThomas : http://catb.org/hacker-emblem (which I recognize to be the "home page" for the emblem) would say that by using this logo (e.g., as your avatar), "you are visibly associating yourself with the hacker culture. This is not quite the same thing as claiming to be a hacker yourself".  Thank you for being a friend to this culture.

"and the most instantly recognizable of all Life patterns." Not me. To me the 5x5 infinite growth is more recognizable. Also, I love CGoL.

Mathematica, 62 bytes
Grid[{{,a=██,},{,,a},{a,a,a}},Frame->All,FrameStyle->Gray]

Mathematica, 71 bytes
Grid[{{,l=Graphics@Disk[],},{,,l},{l,l,l}},Frame->All,FrameStyle->Gray]

I think you can save bytes by using a pure function with Graphics@Disk[] as an argument.

If you mean this Grid[{{,#,},{,,#},{#,#,#}},Frame->All,FrameStyle->Gray]&@Graphics@Disk[]   it is 72 bytes

A 69 byte variant on this: Rasterize@Grid[{{,●,},{,,●},{●,●,●}},Frame->All,FrameStyle->Gray]. The ● is a two byte unicode character. As it doesn't have to strictly be a circle perhaps some ASCII character could work to meet the 40%-80% requirement.

actually I made it to 62 bytes meeting all requirements

I think you need Rasterize or similar to meet the requirement of graphical output but no ASCII art as the symbols are still selectable in the grid as Unicode characters.

OP's words (see main comments):"By "no ASCII art" I meant you couldn't draw the entire thing in ASCII. I didn't mean you can't use Unicode characters :P "

I though Mathematica had a Glider builtin for this, seeing as Stephen Wolfram loves cellular automata so...

CSS+HTML, 56+84=140 bytes 52+84=136 bytes
Saved 4 bytes by incorporating suggestions from the comments.

td{border:1px solid#888;line-height:.4;font-size:3em

•
•
•••

This uses the UTF-8 character • which is 2 bytes long and takes advantage of the graciousness of HTML syntax.

Not bad; that's pretty clever using •!

I could alternatively set border:1px solid#888 which isn't exactly gray but it saves a byte because it omits the space.

By "gray" I didn't mean the HTML builtin color for gray, I meant... generic gray. #888 is fine.

Very cool. I think you could save a byte with line-height:.4;, and another by omitting the closing }

You could save 1 byte with: font-size:3em

Doesn't using • count as ASCII art, which is against the challenge rules?

Are the double borders (internal, Chrome) according to the rules?

@ErnodeWeerd It doesn't say you can't do that, so... yes?

@Hankrecords not sure; OP said it was clever, and ASCII art usually involes ASCII, which • is not (• is unicode but not ASCII)

@StephenS Sounds like a really futile distinction, plus I don't see the sense in banning ASCII art but allowing unicode art. As for the OP saying it was clever, my comment was in fact directed to the OP, I forgot to put in the @.

@StephenS By "no ASCII art" I meant you couldn't draw the entire thing in ASCII. I didn't mean you can't use Unicode characters :P

You can save characters by making the grey border 4px, and ditching the font-size. Then it meets the size requirements. (It's a bit ambiguous in the description whether the gridlines are counted in the 40-80% requirement. I'm assuming they're not.)

The only Code Golf answer I ever understood, at least I do believe so.

I zoomed the result on my display (Firefox, possibly flawed), I count cells of 19*21 = 399 pixels. Of those, I count 261 white pixels (rendered red in the picture for easy counting). So that's (399 - 261) / 399 = 34.58% of covered area. And therefore I believe this answer doesn't meet the criteria. In short, the dots are too small.

Well if it's OP-approved (OPproved?) +1 then :) but @MDXF please edit your question to make it clearer, someone else might misunderstand that rule as I did

You can save 4 bytes by removing 1px

GLSL (fragment shader), 278 235 256 bytes
precision highp float;void main(){vec2 a=gl_FragCoord.xy/20.-.2;ivec2 b=ivec2(a);a-=vec2(b)+.5;if(b.x>2||b.y>2)discard;gl_FragColor=a.x<-.5||a.y<-.5||a.x>.3||a.y>.3?vec4(.5,.5,.5,1.):length(a+.1)<.4&&(b.x+b.y-3)*b.y==0?vec4(0.,0.,0.,1.):vec4(1.,1.,1.,1.);}

See it in action: http://glslsandbox.com/e#40717.2

Hmm... does the border go all the way around the graphic? Also, are you sure the circles are only 80% of the grid-boxes?

A circle that touches all four sides of its enclosing square fills a proportion π/4 of the square, or about 78.5%.

I reduced your code's size to 222 bytes:
precision highp float;void main(){vec2 a=gl_FragCoord.xy/20.-.2;ivec2 b=ivec2(a);a-=vec2(b)+.5;gl_FragColor=vec4(vec3(a.x<-.5||a.y<-.5||a.x>.3||a.y>.3?.5:length(a+.1)<.4&&(b.x+b.y-3)*b.y==0?.0:1.)*float(b.x<3&&b.y<3),1.);}

Python 2,  169 140  137 bytes
from turtle import*
up()
shape("square")
color("gray",[1]*3)
i=9
while i:i-=1;goto(20-i/3*20,i%3*20-20);stamp();209&2**i or dot(15,0,0,0)

Actual size, 61 by 61, plotted within a much larger canvas of 300 by 400:

Showing the pixel grid:

The dots use 177 pixels within the range of 40%-80% whether considering the 19 by 19 white fill (144.4-288.8) or the 21 by 21  including both borders (176.4-352.8).
Note: the program terminates and closes the canvas window as soon as the drawing has been finished, to allow manual window closure append the line done().
turtle is a Python package developed for introductory graphical programming. A pen starts at x,y=0,0 in the middle of a 300 by 400 pixel canvas (by default), up lifts the pen, goto moves the pen, shape sets the shape of the pen to a named shape ("square" is a predefined shape with a default pixel-width of 21), color sets the colour, where two parameters set stroke (with default a width of 1) and fill; a byte is saved by using the (r,g,b) tuple option to replace "white" with [1,1,1] using the list multiplication [1]*3. Finally dot draws a dot with the given width in pixels and colour. The dot's default width value is too small to qualify, as is 9 so I made it an aesthetic and qualifying 15. The dot's colour could be "black" but the unpacked (r,g,b) tuple of 0,0,0 is shorter by two bytes.
The pen must move away from any dot at the end since otherwise the gray/white pen hides the dot.
The grid is traversed using a div (/) and mod (%) of i starting at 8 (i is initialised to 9 but is decremented at the beginning of the while loop) and working down to 0, offsetting the results of 2,1,0 to -1,0,1 using (1-...) and scaling each up to the grid size using a factor of 20 (note that 20-i/3*20 is actually a byte less than 20*(1-i/3), same goes for %). This produces the order [bottom-left, centre-left, top-left, bottom-middle, centre-middle, top-middle, bottom-right, centre-right, top-right], and requires a "hasDot" evaluation of [1,0,0,1,0,1,1,1,0], which is 302 in binary so may be accessed by inspecting the ith power of two component of 302 with using a bitwise-and, 302&2**i. This can then be inverted to 209&2**1 enabling the use of or rather than and.

Very good approach and much shorter than I thought would be possible using turtle.

HTML & CSS, 155 bytes
Turns out HTML is really forgiving about syntax errors.
1 byte saved thanks to @Octopus · 1 byte saved thanks to @Downgoat · 2 bytes saved thanks to @StephenS
2 bytes saved thanks to @Ryan · 3 bytes saved thanks to @styfle · 4 bytes saved thanks to @Ferrybig
13 bytes saved thanks to @SteveBennett

p{height:33px;width:33px;border-radius:50%;background:#000;margin:0

<p

You can save quite a few bytes by removing the td CSS definition, and changing the
 to <table cellspacing=0 border=1 .... The output is slightly different, but it looks close enough to my eyes. shrug

Weirdly, I can replace most of the
 to
 and it works, but if I replace all of them, it doesn't. Particularly the one in the top row - if I change it, they all disappear. Chrome 58.0.3029.110 (64-bit), OSX.

It actually looks like a bug in Chrome. In the inspector, if I disable one of the style properties, and re-enable, the table gets redrawn correctly.

@SteveBennett thanks! about the p thing, I've tried it before, doesn't work on my chrome 59.. (circles get ellipse shape)

border=1 will be correct and save two bytes over border=1px.

To get p to look good, you have to add ;margin:0 which adds 9 bytes but saves you 12 bytes over div so a net of 3 bytes saved.

You don't need the first
, saving another 4 bytes

Applesoft BASIC, 479 476 516 515 483 482 bytes
-32 by using unreadable variable names :P
-1 because Apple decided to be magical and let me use an implicit/nonexistent GOTO
Here is my own (very beatable) program for an example of an output that does not use circles:
1GR:POKE49234,0:COLOR=15:FORI=0TO39:VLIN0,47ATI:NEXT:COLOR=5:S=21:W=S:H=27:X=INT((40-W)/2):Y=INT((48-H)/2):D=INT(W/3):DX=D:C=Y+H:G=X+W:FORI=0TO3:VLINY,C ATX+I*D:NEXT:D=INT(H/3):FORI=0TO3:HLINX,G ATY+I*D:NEXT:YH=INT(D/2):Z=Y+H-YH:XH=INT(DX/2):COLOR=0:FORI=0TO2:B=Z:A=X+XH+I*DX:GOSUB5:NEXT:B=B-D:GOSUB5:B=B-D:A=A-DX:GOSUB5:K=PEEK(-16384):IFK<128THEN2:K=PEEK(-16368):TEXT:HOME:END
5VLINB+2,B-3ATA:VLINB+2,B-3ATA-1:VLINB+2,B-3ATA+1:VLINB+2,B-3ATA+2:VLINB,B-1ATA-1:VLINB,B-1ATA+2:RETURN

Output:

I am not sure if this is valid, since line 1, being longer than 239 characters, is impossible to enter by normal methods, at least on a //e.

@insert_name_here You have to save it to a file on a disk.

IA-32 machine code, 81 80 bytes
Hexdump:
60 8b f9 b8 50 35 20 20 ab b8 32 35 36 20 ab ab
ab fe 4f fe 33 c9 66 49 51 8a c1 d4 55 50 8a c5
d4 55 5b b2 80 84 c0 74 1f 84 db 74 1b b2 ff 2c
10 3c 35 77 13 93 2c 10 3c 35 77 0c 8d 0c 58 8a
cd b0 e4 d2 e0 79 01 42 92 aa 59 e2 cb aa 61 c3

It's a fastcall function called doit that returns the image in PGM format in the supplied buffer. Usage:
char buf[256 * 256 + 256];
doit(buf);

FILE* f = fopen("k.pgm", "wb");
fwrite(buf, 1, sizeof buf, f);
fclose(f);

Output:

I used 256x256 resolution because it's cool it lets me split the pixel's index in ecx automatically into coordinates y in ch and x in cl. Also, the PGM file format requires the number 255 in the image header.
The inner squares are 54x54 (41% of the cell by area).
Source code (can be compiled by Visual Studio):
    pushad;                 // save all registers
    mov edi, ecx;           // edi is now the pointer to output
    mov eax, '  5P';        // PGM file header
    stosd;                  // store it
    mov eax, ' 652';        // the number 256 and a space
    stosd;                  // store the width
    stosd;                  // store the height
    stosd;                  // store maximum brightness
    dec byte ptr [edi-2];   // fix maximum brightness to 255

    xor ecx, ecx;           // initialize the counter of pixels
    dec cx;                 // to 65535
myloop:
    push ecx;

    mov al, cl;             // al = cl = x coordinate in the image
    _emit 0xd4;             // divide by 85
    _emit 85;               // ah = x cell number, al = x coordinate in cell
    push eax;
    mov al, ch;             // al = ch = y coordinate in the image
    _emit 0xd4;             // divide by 85
    _emit 85;               // ah = y cell number, al = y coordinate in cell
    pop ebx;                // bh = x cell number, bl = x coordinate in cell

    mov dl, 0x80;           // gray pixel value
    test al, al             // is cell boundary (y)?
    je output1;
    test bl, bl;            // is cell boundary (x)?
    je output1;

    mov dl, 255;            // white pixel value
    sub al, 16;
    cmp al, 53;
    ja output1;             // if outside the inner square, output white
    xchg eax, ebx;          // exchange the registers to shorten following code
    sub al, 16;
    cmp al, 53;
    ja output1;             // if outside the inner square, output white

    lea ecx, [ebx * 2 + eax]; // cell index = y * 2 + x
    mov cl, ch;
    mov al, 0xe4;           // load the bitmap for the glider pattern
    shl al, cl;             // shift the needed but into SF
    jns output1;            // the bit was 0? - output white
    inc edx;                // the bit was 1? - change to black

output1:
    xchg eax, edx;
    stosb;                  // output the byte

    pop ecx;
    loop myloop;
    stosb;                  // output an additional gray pixel
    popad;
    ret;

The cell pattern
0 1 0
0 0 1
1 1 1

can be represented by an "optimized" bitmap of 7 bits.
The bits are indexed by the expression y * 2 + x, where (x,y) is the location of the cell. This expression gives the same index to 2 pairs of cells. It's a lucky coincidence that the bit values there are the same.

This is cool, but I make 42*42/85/85 only 24.4%.

@JonathanAllan: The white parts of the cell are only 84x84, but yes, a 42x42 box is only a quarter of a 84x84 box.

I've asked for clarification (if it does go to area I hope it's not too hard to update).

The ruling is in - it's area...

I enlarged the black dots from 42 to 54. It's the minimum, making them 40% by area, and they have already started being ugly...

@The_Server201 suggested mov cl, al; or cl, bl; je output1; instead of test al, al je output1; test bl,

@StephenS This logic doesn't work - it's like using if !cond0 && !cond1 while I need if !cond0 || !cond1

@anatolyg OK :) I had it in suggested edits, didn't want to send it through, but wanted to inform you of it

PNG, 105 100 bytes
    (i.e. this image file)
Considering that HTML and other non-programming languages are allowed,
I was curious to see how much I could golf a plain browser-displayable image, to serve as a baseline comparison.
The result is compressed with the open-source tools optipng (-o7 -zm1-9 -strip all)
and pngwolf.
I also experimented with zopflipng, but the results were bigger.
Other closed-source compression tools might be able to shave off a couple more bytes.
Image data in base64:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAA
fCAAAAAA6xUnlAAAAK0lEQVR42mM4gB8wHPgPAwf+M0DAf4TYqDwp
4Ydp0qg8ofBDqMXKGpXHDwDDq0qBspApTgAAAABJRU5ErkJggg==

Edit I made the dots touch the cell top and bottom, to cause more repetitiveness in the pixel pattern and thus improve the compression ratio.
But don't worry, a dot still only fills (79)/(99) ≈ 78% of its cell.

Non-standard PNG, 88 bytes
As pointed out by @anatolyg,
there is some golfing potential by removing the IEND chunk (12 bytes).
Technically, IEND is required by the standard.
However, each PNG chunk includes its own size and checksum information.
It is therefore not absolutely necessary to have an end-marker.
And indeed, all browsers I tested can display the image without issue.
Unfortunately (or fortunately), this non-standard image is rejected by imgur,
so I cannot actually upload it. This is the base64 encoding:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAA
fCAAAAAA6xUnlAAAAK0lEQVR42mM4gB8wHPgPAwf+M0DAf4TYqDwp
4Ydp0qg8ofBDqMXKGpXHDwDDq0qBspApTg==

I'm not sure if this valid. PNG is not a programming language.

Thank you MD XF.
@DJMcMayhem Neither is HTML. But considering the small size of the image, I submitted it as an interesting outside-the-box entry.

But with HTML, you still have to actually write valid HTML to generate the correct output. This is simply taking the correct output, compressing it, and dumping that into an answer.

@DJMcMayhem Fair enough. However, both formats simply describe a static document,
according to their respective standards.
Neither actually constructs the image. Instead,
you have to open them with a browser which then interprets
and displays them. This step, i.e. the
actual "running" of the program, is identical for both formats.
It seems somewhat arbitrary to allow one but not the other.

@DJMcMayhem Answers in non-programming-languages are technically allowed by consensus. Especially, for fixed-output challenges.

Also, welcome to PPCG, @Meyer! :)

@Martin Ender Thank you :)

I definitely think this is a valid submission

You can remove a few bytes at the end of the file and still have the image. Might depend on a viewer though; I imagine most viewers don't require e.g. the end-chunk to be present.

@anatolyg Thanks for this suggestion. I have expanded the answer.

R (130 119 113 bytes)
plot(c(2,4,6,4,6),c(2,2,2,6,4),an=F,ax=F,xli=(l=c(1,7)),yli=l,xaxs="i",yaxs="i",pch=16,cex=19);grid(3,lt=1);box()

You can use axes=F to replace xaxt="n",yaxt="n". If you double all coordinate values, you save 4 net bytes (but can't use the 1:3,2:3 trick).

@user2390246: thx for the suggestions. I didn't find much time to golf this out. I'm sure there are more possible improvements. I had to add ;box() as axes=F also removes the frame. Also, as axes is a plot parameter and not passed on to par through ... as xaxt/yaxt, it doesn't even need to be spelled out completely.

You can shave off 7 bytes by (1) replacing c(1,7) with a variable (l=c(1,7)); (2) replacing ann with an; (3) replacing [xy]li with [xy]li; and (4) removing the second, redundant argument to grid: grid(3,lt=1). In fact, I’ve taken the liberty of editing this in (under review).

@KonradRudolph Seems relevant: https://codegolf.meta.stackexchange.com/questions/1615/suggested-edits-reject-golfing

@Matt Hm. Seems that this makes sense if it applies fundamentally different optimisations, rather than just implementing straightforward improvements. Fair enough.

@KonradRudolph Could always bring it up in Meta if you feel there is a distinction. I would give the OP a chance though first. He likely hasn't seen your comment.

Matt was right: I was offline for a week. @KonradRudolph: Thx for the great suggestions. However, I saved only 6bytes this way. Did I miss anything?

Python 2.7, 214 311 309 bytes
from matplotlib.pyplot import*
f,x=subplots()
j=(0,0),(1,0),(2,0),(1,2),(2,1)
for i in j:x.add_artist(Circle((i),.4,color='k'))
x.tick_params(labelbottom='off',labelleft='off')
for o in x.spines.values():o.set_edgecolor('gray')
for s in 'xy':exec"x.set_"+s+"ticks((.5,1.5,2.5));"+s+"lim(-.5,2.5);"
grid()

This is my first attempt here at code golf, so I'm sure this can be improved upon. I would have liked to not established the limits, but it appears that matplotlib can't detect that I plotted circles where I did. Without setting xlim() and ylim() it only shows the bottom two circles.
Output:

Edit:Fixed the color of the borders and removed the tick numbers. I must say matplotlib is very densely worded, and not too friendly with changing axis colors.
Shaved off 3 bytes thanks to @DJMcMayhem
Edit:Took off two bytes by setting my ticks as a tuple inside of set_ticks functions.

Sorry, the numbers on the graph are not allowed. Also, is the border gray?

Welcome to the site! This is a nice first answer. :)

Some minor golfs I see: 1) You can join lines 5 and 6 to remove one space and a newline (-2 bytes) 2) if the order of set xy_ticks and xy_lim doesn't matter, you could do something like for s in'xy':exec"x.set_"+s+"ticks(a);"+s+"lim(-.5,2.5);". (I haven't tested that though, so I'm not positive)

Bash + ImageMagick, 233 226 characters
convert xc: +antialias -resize 31 -draw "stroke gray fill none ${r=rectangle} 0,0 10,30$r 20,0 30,30$r 0,0 30,10$r 0,20 30,30stroke #000 fill #000 ${c=circle} 15,5 15,8$c 25,15 25,18$c 5,25 5,28$c 15,25 15,28$c 25,25 25,28" x:

Sample output:

Bash + ImageMagick, 215 characters
convert xc: -resize 31 -draw "stroke gray fill none ${r=rectangle} 0,0 10,30$r 20,0 30,30$r 0,0 30,10$r 0,20 30,30stroke #000 fill #000 ${c=circle} 15,5 15,8$c 25,15 25,18$c 5,25 5,28$c 15,25 15,28$c 25,25 25,28" x:

The question owner not answered yet the anti-aliasing question and some other solutions also have additional gray shades added by the anti-aliasing, so for now this shorter one looks also acceptable.
Sample output:

Ruby, 144 166 164 148 144 bytes
require'ruby2d'
set background:'grey'
c=482;g=2,13,24;d=0
g.map{|y|g.map{|x|Square.new(x,y,9);Square.new(x+2,y+2,5,"black")if c[d]>0;d+=1}}
show

Output:

Edit: Now has grey gridlines.

I can see a few golfing opportunities here. For starters, d=d+1 has a shorter form (d+=1) and ==1 here is equivalent to >0. Also consider what happens when you set c to a Fixnum instead of an array :-)

@RJHunter I missed some low hanging fruit! And I thought about stuff like creating a bitfield with c, but couldn't come up with a way that's actually shorter in implementation.

Perhaps surprisingly, Ruby actually has some bitfield support built in! You can access an integer with [] as if it were an array of bits (read-only, but that's OK here).

@RJHunter thanks! All I had to do was change c to the appropriate number. Reduced it quite a bit.

You can change g=[2,13,24] to g=2,13,24 and replace each with map.

Thanks @Jordan! Haha, all these things I'd never do in real Ruby code.

Octave, 127 124 bytes
Removed 3 bytes thanks to Luis. grid is sufficient (instead of grid on).

spy([0,1,0;0,0,1;1,1,1],'.k',300),set(gca,'XTick',.5:4,'XTickLabel','','YTick',.5:4,'YTickLabel',''),axis(.5+[0,3,0,3]),grid

Outputs (after saving):
Note that the output shows grey grid lines in the plot window. They turn black when saving it (for some reason).

Well, that was long and messy! Had to go through a lot of modifications to make this adhere to the specs.
Explanation and hopefully some golfing coming up...

That doesn't look very squary, but since the OP did an answer with a similar output I guess it's fine..

I think the height/width ratio is platform dependent. Add 'square' to make it square on all platforms. spy([0,1,0;0,0,1;1,1,1],'.k',80),set(gca,'XTick',.5:4,'XTickLabel','','YTick',.5:4,'YTickLabel',''),axis(.5+[0,3,0,3],'square'),grid on.

Tikz, 193 175 170 bytes
\documentclass[tikz]{standalone}\begin{document}\tikz{\def\a{)rectangle(}\def~{)circle(1)(}\draw(4,6\a2,0)(0,\a6,6)(0,2\a6,4);\fill(1,1~3,1~5,1~5,3~3,5~,)}\end{document}

Here is the output:

@MDXF It cuts close but since these are perfect circles and squares they make up π/4 of the square which is just about 79% of the square.

Damn, looked at the challenge and had the exact same idea ;)

Processing, 212 198 bytes
background(-1);noFill();stroke(99);rect(0,0,99,99);rect(0,33,99,33);rect(33,0,33,99);fill(0);int f=25;ellipse(15,83,f,f);ellipse(50,83,f,f);ellipse(83,83,f,f);ellipse(83,50,f,f);ellipse(50,15,f,f);

Basically, we are using simple Processing to draw a white Background, then setting the transparency of the rectangles used for the grid and drawing their strokes in gray. We continue by defining the rectangle, setting the fill value again to fill the circles black and defining fives circles.
Voilà!

You could add
strokeWidth(2);

or more to see the color of the strokes better.
(That's my first Code-Golf, I hope I did everything right.)
EDIT: Thanks to Kritixi Lithos! Removed the newlines, changed to int f=25 and used -1 in background(float)

Great first golf, and welcome to PPCG!

You can save some bytes by removing the newlines, changing the float f=25; to int f=25; and by using background(-1) instead of background(255);

MATL, 74 69 68 64 62 bytes
4:6B&f,.5+w]'k.'5Y08W5$XG7lZG1$3ZG1tZG4:"@th1Kh'k'3$XG1Mbw3$XG

Try it at MATL Online! (It takes a few seconds.)
Output from the online interpreter:

SmileBASIC, 125 bytes
GCLS-1GCOLOR #GRAY
GBOX.,8,30,38GBOX 10,8,20,38GBOX.,18,30,28G.,1G 1,2G-1,3G.,3G 1,3DEF G X,Y
GPUTCHR X*10+12,Y*10,58081,.END

58081 (U+E2E1, which is in the "Private Use Area" block) is the character code for a circle symbol. Putting it in a string would work too, but since it is encoded as 3 bytes in UTF-8, it would be the same length (and not show up correctly here).

It's always refreshing to see [tag:basic] answers!

Processing, 134 bytes
Inspired by @Zep, here's my Processing one-liner (and also my first code golf):
stroke(127);for(int i=0;i<9;i++){int x=i%3*30;int y=i/3*30;fill(255);rect(x+5,y+5,30,30);if(i==1||i>4){fill(0);ellipse(x+20,y+20,23,23);}};

Thanks for @Kritixi Lithos for his brilliant tips on shaving a few bytes off!

Please include a Language and Score line.

Welcome to PPCG and nice first answer! As stated by Theraot, all submissions are required to have a header that contains the language and the bytecount. Also, you can golf some bytes by declaring all the variables in the for-loop, like for(int i=0,x,y;... so that you can drop the int in the body of the loop. Additionally, fill(255); can become just fill(-1);

I haven't tested this, but I think you can drop the +20 while setting the values of x and y, so that you can change the rect to rect(x+5,y+5,30,30); and the ellipse to ellipse(x+20,y+20,23,23); and save two bytes.

LaTeX + Tikz, 155 bytes
\documentclass[tikz]{standalone}\begin{document}\tikz{\draw(0,0)grid(3,3);\foreach\x/\y in{0/0,1/0,2/0,2/1,1/2}\fill(\x+.5,\y+.5)circle(.5);}\end{document}

Is that 1/0 doing division?

Sorry, but no, it is a simple separator in the pairs of x and y coordinates.

You should be able to save a few bytes by scaling everything up by 2, so that your .5's become just 1.

@Chris The grid builtin produces unit-length cells by default, and it takes about 8 bytes more (adding [step=2]) to produce a larger grid. Simplifying the coordinates for the circles drawing would not save enough bytes. (You get 157 bytes if you do it)

@TonioElGringo Ah, true. Teaches me to comment without testing it first ;)

@TonioElGringo Chris seems to be right after all. I did a plain TeX solution that is basically yours without looking at your solution. ("Great minds think alike.") Anyways: Scaling up by 2 will save you a couple of bytes since you can leave out all the 1s in co-ordinate entries: \documentclass[tikz]{standalone}\begin{document}\tikz{\draw[step=2](0,0)grid(6,6);\foreach\x/\y in{/,3/,5/,5/3,3/5}\fill(\x,\y)circle();}\end{document} will leave you with 151 bytes. Further improvements: See my solution.

@TonioElGringo Here's your solution down to 143 bytes with some generic improvements:
\documentclass[tikz]{standalone}\def~{document}\begin~\tikz\filldraw[step=2](0,0)grid(6,6)\foreach~/\1in{/,3/,5/,5/3,3/5}{(~,\1)circle()};\end~. PLUS: You can save more if you don't use the \foreach-loop. This will be only 133 bytes: \documentclass[tikz]{standalone}\def~{document}\begin~\tikz{\def~{)circle()(}\filldraw[step=2](0,0)grid(6,6)(,~3,~5,~3,5~5,3~,)}\end~. Note that this is basically my answer but in LaTeX.

Desmos, 90 characters/bytes
Nobody saw that one coming.

(x-3)^2+(y-1)^2<=1
(x-1)^2+(y-1)^2<=1
(x-5)^2+(y-1)^2<=1
(x-5)^2+(y-3)^2<=1
(x-3)^2+(y-5)^2<=1

Graph link

Thanks pydude for reminding me about inequalities!
If needed, I can crop the exported Desmos PNG to remove the axis/unneeded graph space

I'm pretty sure that you can fill in the region using an inequality. It will take some more equations to get to work, though.

@pydude I made the circles inequalities. Thanks.

A lot of bytes can be saved by rearranging and combining the equations: Graph link

38 bytes

@AsoneTuhid my gosh you madlad. I'll edit when I get the chance.

i like this, but i dont think its valid. either youre outputting only the circles, which fails to include the grid, or you're including the whole grid with the numbers, which fails the "Nothing else." requirement. if you're allowed to zoom into a specific part of the grid and only count that part, that seems hard to score...

R 313 236 bytes
l=c(1,7)
bmp('r',400,400)
plot(x=c(4,6,2,4,6),c(6,4,2,2,2),cex=14,pch=19,xlim=l,ylim=l,xlab='',ylab='',xaxs='i',yaxs='i',axes=F)
a=function(s,...){axis(s,labels=F,tck=1, col="gray",lwd=9,...)}
a(1,xaxp=c(l,3))
a(2,yaxp=c(l,3))
dev.off()

Revised Code, based mostly on comments from @user2390246 saved 77 bytes.  Much appreciate the help.
Also, want to add a call-out to the shorter R solution by @mschilli. It shows an elegant way of addressing the problem of the R graphical defaults.
Output
Goes to a file names "r". Omitting and extension saved me four bytes. But then I had to edit the file extension back to "bmp" to get it to upload. So maybe I should not get credit for those four bytes.

Commentary
I thought to myself, "That graphic's nothing but a simple graph with five dots. Should be a simple matter to implement in a language with robust data graphics capabilities. Let's try it in R."  But then I had to spend on the order of 80% of the bytes just working around R's graphic defaults.
Kinda the wrong tool for the job....
I could not find a less verbose way of guaranteeing the size and aspect ratio of the graphic than by calling the bmp constructor and then dev.off(). The combination accounts for 25 bytes.  If your R session is set up right, you might get a graphic that looks more or less correct without spending those bytes, but it's not reliable or consistent.

A few suggestions: don't define your data frame b at all, just put the two vectors unnamed directly into the plot function. Use c(l,3) for xaxp and yaxp. You can just use F for false, no need to define it. And define a new function a(x,...) that calls axis(x) but has default values for labels, tck, etc, so that you don't need to specify them all twice.

You can also use axes=F rather than xaxt='n',yaxt='n'. And you forgot to use .5 when defining l though you did use that trick later.

Actually, what if you doubled all coordinate values to get rid of those pesky .5s altogether?

If you're already gonna take off the .bmp file extension, you might as well just name the file r.

@user2390246 -- Many thanks for the suggestions.  Cumulatively saved on the order of 70 bytes.  This  was my first effort at golfing in R, and you spotted a lot of tyro's errors I probably should have seen.  Next time I'll know better.

I don’t think you need the code to save this to a file, other answers don’t have it either.

I agree @Konrad Rudolf that the added code to save to a file is not strictly necessary, but for now I'll leave it in. mschilli has an elegant solution without the extra code (which is shorter than this one).  But I could not generate output that I was satisfied with without it.  The lwd = 9 is also mostly here for aesthetic purposes, and so could also be removed.

C#, 333 bytes
using System.Drawing;_=>{var b=new Bitmap(31,31);var g=Graphics.FromImage(b);g.FillRectangle(Brushes.White,0,0,31,31);for(int x=0,y;x<3;++x)for(y=0;y<3;++y){g.DrawRectangle(Pens.Gray,x*10,y*10,10,10);if(x==1&y<1|x>1&y==1|y>1)g.FillEllipse(Brushes.Black,x*10+1,y*10+1,8,8);}b.Save("t.png");System.Diagnostics.Process.Start("t.png");};

Full/Formatted version:
using System.Drawing;
Action<int> a = _ =>
{
    var b = new Bitmap(31, 31);
    var g = Graphics.FromImage(b);

    g.FillRectangle(Brushes.White, 0, 0, 31, 31);

    for (int x = 0, y; x < 3; ++x)
        for (y = 0; y < 3; ++y)
        {
            g.DrawRectangle(Pens.Gray, x * 10, y * 10, 10, 10);
            if (x == 1 & y < 1 | x > 1 & y == 1 | y > 1)
                g.FillEllipse(Brushes.Black, x * 10 + 1, y * 10 + 1, 8, 8);
        }

    b.Save("t.png");
    System.Diagnostics.Process.Start("t.png");
};

Idea is simple we create a graphics object from the image and use that to make the image all white. Then loop over each square of the image drawing the bounding rectangle. If it is one of the locations for a dot we draw that too. Lastly we save the image to file and let windows decide how to open it, in my case it opens with Windows Photo Viewer.
Using a Process to show the image and saving to file is a lot shorter than creating a windows forms or WPF app because of all the different classes and extra fluff needed to create them.
Output:

I don't think you have to use Process.Start, for example Titus answer just outputs image, doesn't open it

@JeremyThompson the rules state you must display the image

PHP, 221+2 bytes
<?imagefill($i=imagecreatetruecolor(31,31),0,0,~0);function q($i,$x,$y,$f){imagerectangle($i,$x*=10,$y*=10,$x+10,$y+10,5e6);$f?:imagefilledellipse($i,$x+5,$y+5,8,8,0);}for($p=16;$p--;)q($i,$p&3,$p>>2,53>>$p&1);imagepng($i);

Save to file; call in browser. Should your browser display gibberish,
insert header("Content-Type:image-png"); before imagepng($i);.
 The dots aren´t very round; that´s due to the small proportions.
breakdown
function q($i,$x,$y,$f){        # function to draw quadrant:
    imagerectangle($i,$x*=10,$y*=10,$x+10,$y+10,5e6);   # draw lines in 0x4c4b40
    $f?:imagefilledellipse($i,$x+5,$y+5,8,8,0);         # if bit not set, draw dot
}

imagefill($i=
    imagecreatetruecolor(31,31) # create image
    ,0,0,~0);                   # fill with 0xffffff (white)
for($p=16;$p--;)q($i,           # loop through positions, call function
    $p&3,                           # $x=lower two bits
    $p>>2,                          # $y=upper two bits
    53>>$p&1                        # $f=one bit from %0000 0000 0011 0101
);                                      # (reversed inverted bit mask for dots)
imagepng($i);                   # output image

I think that 0x4c4b40 qualifies as approximated gray. If not, add four bytes.

TI-BASIC, 218 140 137 bytes
Note: this byte count is a bit inflated. The program only uses 85 tokens, but that's saved in 137 bytes. Also, if you're using a non-color calculator, you could save another 4 tokens because you don't have to specify the color (which is blue by default), but I don't know if their screens are large enough for this challenge because I don't own one.
prgmHACKER (138 bytes, 86 tokens):
{0,1,2,1,2}→L1
{0,0,0,2,1}→L2
.3→R
For(I,0,5
L₁(I)+.5→A
L₂(I)+.5→B
For(N,0,R,dX
√(R²-N²
Line(A+N,B-Ans,A+N,B+Ans,BLACK
Line(A-N,B-Ans,A-N,B+Ans,BLACK
End
End

For proper display, this program requires that Xmin = Ymin = 0, Xmax = Ymax = 3, Xscl = Yscl = 1. dX also needs to be properly set, but the calculator does that for you when you set any other window variable. I couldn't see how much space these variables used in RAM.
Furthermore, the format settings should be { RectGC, CoordOff, GridLine, GridColor:MEDGRAY, Axes:OFF, ExprOff, BorderColor:1, Background:OFF } but these are toggled values and don't consume any extra space depending on the setting.

To golf down the list, you could express these as {0,1,2,1,2} and {0,0,0,2,1} then add 0.5 to each item.

Tried that, but it didn't change the amount of RAM used by the lists. However, you gave me the idea to get rid of the lists in RAM and define them when the program runs, saving 78 bytes in RAM.

Order in the golf people!
Change the L1 and L2 to L₁ and L₂, since you already use them.
I think you can remove }, and L₁(I)+.5→A=>.5+L₁(I and L₂(I)+.5→B => .5+L₂(I→B.

R, 119 114 111 bytes
(Thans to @JayCe for saving me 3 bytes)
Not shorter than the other R solution, but a completely different approach:
layout(matrix(c(1,9,2:8),3,,T))
par(mar=0*1:4)
for(i in 1:9){frame();if(i>4)points(.5,.5,,19,cex=29);box(fg=8)}

Uses a layout in which the 4 first plots are the empty cells, and the 5 others the ones with dots in them.

Can me made shorter than the other solution using positional arguments: here

@JayCe indeed, thanks!

Excel VBA,  180 bytes
An anonymous VBE Immediate window function that takes no input and outputs to the default worksheet object (Sheet1).
Cells.RowHeight=48:Cells.Interior.Color=-1:[B2:D4].Borders.Color=7434613:For Each s In[{21,32,13,23,33}]:Sheet1.Shapes.AddShape(9,48*(s\10)+4,Mid(s,2)*48+4,40,40).ShapeStyle=8:Next

Commented Version
Cells.RowHeight=48                      ''  Make the Cells square
Cells.Interior.Color=-1                 ''  Remove the default grid
[B2:D4].Borders.Color=7434613           ''  Create the 3x3 gray grid at range `B2:D4`
For Each s In [{21,32,13,23,33}]        ''  Assign and iterate over a list of where
                                        ''  to place the dots; This list is a range that
                                        ''  is coerced into a numeric array

    Sheet1.Shapes.AddShape(         _   ''  Add a shape to the Sheet1 object
                9,                  _   ''  An Oval (msoShapeOval)
                48*(s\10)+4,        _   ''  At the calculated value from the left
                Mid(s,2)*48+4,      _   ''  At the calculated value from the top
                40,                 _   ''  that is 40 units wide
                40                  _   ''  and is 40 units tall
                ).ShapeStyle=8          ''  and is black filled with a black outline

Next                                    ''  repeat to end of list

Output

-6 bytes for using [{21,32,13,23,33}] over  Split("21 32 13 23 33")
-2 bytes for using Mid(s,2) over Right(s,1)
-3 bytes for using (s\10) over Left(s,1)

gnuplot, 158 156 bytes
se si ra-1
se xti 2
se yti 2
se st l 9 lc"gray"lt 1
se gr xt ls 9 yt ls 9
se bor ls 9
uns k
se form""
p'-'w cir fc"black"fs s
3 5 1
5 3 1
1 1 1
3 1 1
5 1 1

Because the radii are the same, I tried the following:
[... first eight lines same as above ...]
p'-'u 1:2:(1) w cir fc"black"fs s
3 5
5 3
1 1
3 1
5 1

But that lead to the same byte count.
Ungolfed with comments:
set size ratio -1                                     # make it a square.
set xtics 2                                           # vertical lines every 2 units
set ytics 2                                           # horizontal lines every 2 units
set style line 9 linecolor rgb "gray" linetype 1      # define line type 9 as solid gray
set grid xtics linestyle 9 ytics linestyle 9          # make internal lines of type 9
set border linestyle 9                                # make outside lines of type 9
unset key                                             # no legend please
set format ""                                         # no numbers next to axes please
plot '-' with circle fillcolor rgb "black" fill solid # plot black circles
3 5 1                                                 # at these locations
5 3 1
1 1 1
3 1 1
5 1 1
end

Are the circles <= 80% of the grid-boxes?

Maybe by area, /4 ≈ 78%

Yes, they cover pi/4, which is around 78%. I could make them slightly smaller and look better, but that would cost byte.

... would cost one byte.

Apple Swift (iOS - CoreGraphics/QuartzCore) - 832 Bytes
I drew the shape entirely using Quartz for an Apple iOS device. Unfortunatly this isn't a particularly size mindful language so the result is quite large, but this is as small as I can get it.
UIGraphicsBeginImageContext(CGSize(width:60,height:60));let c=UIGraphicsGetCurrentContext()!;UIColor.lightGray.setStroke();c.addRect(CGRect(x:0,y:0,width:60,height:60));c.move(to: CGPoint(x:20,y:0));c.addLine(to: CGPoint(x:20,y:60));c.move(to: CGPoint(x:40,y:0));c.addLine(to: CGPoint(x:40,y:60));c.move(to: CGPoint(x:0,y:20));c.addLine(to: CGPoint(x:60,y:20));c.move(to: CGPoint(x:0,y:40));c.addLine(to: CGPoint(x:60, y:40));c.strokePath();UIColor.black.setFill();c.addEllipse(in:CGRect(x:22,y:2,width:16,height:16));c.addEllipse(in:CGRect(x:42,y:22,width:16,height:16));c.addEllipse(in:CGRect(x:2,y:42,width:16,height:16));c.addEllipse(in:CGRect(x:22,y:42,width:16,height:16));c.addEllipse(in:CGRect(x:42,y:42,width:16,height:16));c.fillPath();let i=UIGraphicsGetImageFromCurrentImageContext();sub.addSubview(UIImageView(image:i))

A more readable version for anyone that is interested:
    UIGraphicsBeginImageContext(CGSize(width: 60, height: 60))
    let c = UIGraphicsGetCurrentContext()!

    UIColor.lightGray.setStroke()
    c.addRect(CGRect(x: 0, y: 0, width: 60, height: 60))
    c.move(to: CGPoint(x: 20, y: 0))
    c.addLine(to: CGPoint(x: 20, y: 60))
    c.move(to: CGPoint(x: 40, y: 0))
    c.addLine(to: CGPoint(x: 40, y: 60))
    c.move(to: CGPoint(x: 0, y: 20))
    c.addLine(to: CGPoint(x: 60, y: 20))
    c.move(to: CGPoint(x: 0, y: 40))
    c.addLine(to: CGPoint(x: 60, y: 40))
    c.strokePath()

    UIColor.black.setFill()
    c.addEllipse(in: CGRect(x: 22, y: 2, width: 16, height: 16))
    c.addEllipse(in: CGRect(x: 42, y: 22, width: 16, height: 16))
    c.addEllipse(in: CGRect(x: 2, y: 42, width: 16, height: 16))
    c.addEllipse(in: CGRect(x: 22, y: 42, width: 16, height: 16))
    c.addEllipse(in: CGRect(x: 42, y: 42, width: 16, height: 16))
    c.fillPath()

    let i = UIGraphicsGetImageFromCurrentImageContext()
    sub.addSubview(UIImageView(image: i))

Here is the output produced in the iOS Simulator:

Ruby with Shoes, 118 characters
Shoes.app{9.times{|i|
stroke gray
fill white
rect x=i%3*w=10,y=i/3*w,w,w
stroke fill black
oval x+2,y+2,7if 482[i]>0}}

Bit checking borrowed from RJHunter's comment made on Mark Thomas's Ruby solution.
Sample output:

Octave 107 94 103 bytes
scatter([1:3,3,2],[1,1,1:3],1e5,zeros(5,3),'.');axis off;a=[1,7]/2;for i=.5:3.5;line(a,i);line(i,a);end

or
plot([1:3,3,2],[1,1,1:3],'k.','markersize',250);axis off;a=[1,7]/2;for i=.5:3.5;line(a,i);line(i,a);end

both solutions are 103 bytes.

I believe the dots have to be black

ah, interesting, matlab interprets a scalar as a point, as opposed to broadcasting it. so instead of line([x1,x2],y) you have to do line([x1,x2],[y,y])

SVG + HTML, 146 138 135 133 bytes

<svg>
<path fill=none stroke=#ccc d=m.5.5h30v30h-30zh10v30h10v-30zv10h30v10h-30 />
<path d=m11,1h9v9m1,1h9v9m-29,1h9v9m1-9h9v9m1-9h9v9>

Thanks to the lenient HTML parser, the xmlns="http://www.w3.org/2000/svg" declaration
can be omitted, as well as attribute quotes and the closing tag.
This version addresses some issues of other HTML solutions:

A "dot" is guaranteed to fill at least 40% of its cell (§1)
It does not use external graphics, e.g. font glyphs (§2, §6)
The grid is perfectly square (§5)

Edit Shaved off 8 bytes by exploiting the fact that subpaths do not
need to be closed in order to be filled.
Edit According to the svg grammar,
it is not necessary to separate non-digits with a comma (.5,.5).
I also abandoned the explicit fill, so that I can change the draw direction
of one line from negative to positive, saving a dash. And lastly,
I adjusted the count to not include a trailing newline, sorry for this rookie mistake.
Edit Learning from another answer on codegolf,
I have replaced the final 9 /> with 9>. This works fine in all tested browsers.

OpenTuring, 203 189 bytes
This is a language I hated in high school, but it has graphics, so...
for i:0..2
for j:0..2
Draw.Box(i*30,j*30,(i+1)*30,(j+1)*30,28)
if j=0 then
Draw.FillOval(i*30+15,15,12,12,7)
end if
end for
end for
Draw.FillOval(45,75,12,12,7)
Draw.FillOval(75,45,12,12,7)

@MDXF, oh sorry, I just remembered about the screenshot, I'll add it now

No problem; thanks! +1

Python 3 + PyGame, 238 Bytes
from pygame import*
d=display
s=d.set_mode([55]*2)
s.fill([255]*3)
f=lambda*a:draw.line(s,[128]*3,*a)
for x in[0,18,36,54]:
 f((x,0),(x,55));f((0,x),(55,x))
for x,y in zip([3,5,1,3,5],[1,3,5,5,5]):draw.circle(s,[0]*3,(x*9,y*9),7)
d.flip()

The seemingly arbitrary constants are from me trying to make the image as large as possible without sacrificing bytes.
With non-printing characters, 229 Bytes
from pygame import*
d=display
s=d.set_mode([55]*2)
s.fill([255]*3)
f=lambda*a:draw.line(s,[128]*3,*a)
for x in b'\0␒$6':
 f((x,0),(x,55));f((0,x),(55,x))
for x,y in zip(b'␃␅␁␃␅',b'␁␃␅␅␅'):draw.circle(s,[0]*3,(x*9,y*9),7)
d.flip()

I replaced the lists with byte literals, which saves a bunch of characters, but the non-printing characters can't be displayed properly here. I substituted the non-printing characters with their graphical equivalents U+2400-U+2421 for display purposes.

Postscript (72 bytes)
00000000: 924e 337b 924e 337b 3088 0034 2034 9281  .N3{.N3{0..4 4..
00000010: 3420 3092 ad7d 9283 924d 3020 3492 ad7d  4 0..}...M0 4..}
00000020: 9283 924d 337b 3188 0132 2032 9280 3420  ...M3{1..2 2..4
00000030: 3092 ad7d 9283 327b 88fc 3492 ad31 8801  0..}..2{..4..1..
00000040: 3220 3292 807d 9283                      2 2..}..

Text version:
gsave 3{gsave 3{0 0 4 4 rectstroke 4 0 translate}repeat grestore 0 4 translate}repeat grestore 3{1 1 2 2 rectfill 4 0 translate}repeat 2{-4 4 translate 1 1 2 2 rectfill}repeat

More readable text version:
/R {1 1 2 2 rectfill}def
gsave
3 {gsave 3 {0 0 4 4 rectstroke 4 0 translate} repeat grestore 0 4 translate} repeat
grestore
3 {R 4 0 translate} repeat
2 {-4 4 translate R} repeat

These don't render correctly for me. (And the last one looks rather different to the first two).

it should be correct now

Yes, thanks! (I guess your interpretation for the gridline colors is that black is a shade of gray :-)

6502 machine code (C64), 310 bytes
00 C0 A9 00 A8 85 FC A2 E0 86 FD A2 20 91 FC C8 D0 FB E6 FD CA D0 F6 A2 CC 86
FD A2 18 A0 27 C0 09 F0 20 C0 06 F0 1C C0 03 F0 18 C0 00 F0 14 E0 18 F0 10 E0
15 F0 0C E0 12 F0 08 E0 0F F0 04 A9 01 D0 02 A9 C1 91 FC 88 10 D5 A5 FC 69 27
85 FC A5 FD 69 00 85 FD CA 10 C4 A9 FF A2 0F 9D 60 E1 9D A0 E2 9D 38 E5 9D 78
E6 9D C8 E8 9D 08 EA 9D E0 E8 9D 20 EA 9D F8 E8 9D 38 EA CA 10 DF A9 40 85 FC
A9 E1 85 FD A2 20 A0 00 A9 18 91 FC C8 98 29 07 D0 F6 98 69 10 A8 CA F0 13 8A
29 03 D0 EA A5 FC 69 40 85 FC A5 FD 69 01 85 FD D0 DA A9 18 A2 02 9D 05 E0 9D
1D E0 9D 35 E0 9D 4D E0 9D 40 EB 9D 58 EB 9D 70 EB 9D 88 EB CA 10 E5 A9 0B 85
FC A9 E0 85 FD A2 20 A0 00 A9 FF 91 FC C8 98 29 01 D0 F6 98 69 06 A8 CA F0 13
8A 29 07 D0 EA A5 FC 69 C0 85 FC A5 FD 69 03 85 FD D0 DA A9 1F A2 01 9D 03 E0
9D C3 E3 9D 83 E7 9D 43 EB 49 E7 9D 4B E0 9D 0B E4 9D CB E7 9D 8B EB 49 E7 CA
10 E1 A9 94 8D 00 DD A9 3B 8D 11 D0 A9 3D 8D 18 D0 A9 01 8D 20 D0 D0 FE

I expected this to be possible in fewer bytes, but didn't find a way for quite a while, so posting it anyways now :)
Online demo, Usage: sys49152

Explanation: (commented vice disassembly listing)
         00 C0       .WORD $C000

         ; clear 8kiB monochrome bitmap at $E000:
         ;---------------------------------------
.C:c000  A9 00       LDA #$00
.C:c002  A8          TAY
.C:c003  85 FC       STA $FC
.C:c005  A2 E0       LDX #$E0
.C:c007  86 FD       STX $FD
.C:c009  A2 20       LDX #$20       ; 16 pages to clear
.C:c00b   .cl_loop:
.C:c00b  91 FC       STA ($FC),Y
.C:c00d  C8          INY
.C:c00e  D0 FB       BNE .cl_loop
.C:c010  E6 FD       INC $FD
.C:c012  CA          DEX
.C:c013  D0 F6       BNE .cl_loop

     ; set colors for each 8x8 pixel block:
         ; ------------------------------------
.C:c015  A2 CC       LDX #$CC       ; start address of bitmap colors
.C:c017  86 FD       STX $FD        ; ($CC00) in zeropage pointer
.C:c019  A2 18       LDX #$18       ; 25 rows
.C:c01b   .sc_loop1:
.C:c01b  A0 27       LDY #$27       ; 40 columns (counts down to 0)
.C:c01d   .sc_loop2:
.C:c01d  C0 09       CPY #$09       ; for every third column ...
.C:c01f  F0 20       BEQ .sc_gray
.C:c021  C0 06       CPY #$06
.C:c023  F0 1C       BEQ .sc_gray
.C:c025  C0 03       CPY #$03
.C:c027  F0 18       BEQ .sc_gray
.C:c029  C0 00       CPY #$00
.C:c02b  F0 14       BEQ .sc_gray
.C:c02d  E0 18       CPX #$18       ; ... or every third row ...
.C:c02f  F0 10       BEQ .sc_gray
.C:c031  E0 15       CPX #$15
.C:c033  F0 0C       BEQ .sc_gray
.C:c035  E0 12       CPX #$12
.C:c037  F0 08       BEQ .sc_gray
.C:c039  E0 0F       CPX #$0F
.C:c03b  F0 04       BEQ .sc_gray   ; ... use gray ($C) on white ($1)
.C:c03d  A9 01       LDA #$01       ; else use black ($0) on white ($1)
.C:c03f  D0 02       BNE .sc_store
.C:c041   .sc_gray:
.C:c041  A9 C1       LDA #$C1
.C:c043   .sc_store:
.C:c043  91 FC       STA ($FC),Y    ; store color
.C:c045  88          DEY
.C:c046  10 D5       BPL .sc_loop2
.C:c048  A5 FC       LDA $FC        ; add offset for next row to pointer
.C:c04a  69 27       ADC #$27
.C:c04c  85 FC       STA $FC
.C:c04e  A5 FD       LDA $FD
.C:c050  69 00       ADC #$00
.C:c052  85 FD       STA $FD
.C:c054  CA          DEX
.C:c055  10 C4       BPL .sc_loop1

         ; draw "dots" as solid squares of 16x16
         ; -------------------------------------
.C:c057  A9 FF       LDA #$FF       ; all 8 pixels set
.C:c059  A2 0F       LDX #$0F       ; loop over 2*8 = 16 rows
.C:c05b   .sq_loop:
.C:c05b  9D 60 E1    STA $E160,X    ; store at different offsets
.C:c05e  9D A0 E2    STA $E2A0,X    ; this works because of the strange
.C:c061  9D 38 E5    STA $E538,X    ; VIC-II framebuffer memory layout:
.C:c064  9D 78 E6    STA $E678,X    ;
.C:c067  9D C8 E8    STA $E8C8,X    ; 8 consecutive bytes form a block
.C:c06a  9D 08 EA    STA $EA08,X    ; of 8x8 pixels.
.C:c06d  9D E0 E8    STA $E8E0,X    ; The next 8x8 block is to the right
.C:c070  9D 20 EA    STA $EA20,X    ; of the previous one.
.C:c073  9D F8 E8    STA $E8F8,X    ; after 40 of these blocks, a new row
.C:c076  9D 38 EA    STA $EA38,X    ; starts.
.C:c079  CA          DEX
.C:c07a  10 DF       BPL .sq_loop

         ; draw vertical grid lines:
         ; -------------------------
.C:c07c  A9 40       LDA #$40       ; load vector with
.C:c07e  85 FC       STA $FC        ; start address of the
.C:c080  A9 E1       LDA #$E1       ; first grid line
.C:c082  85 FD       STA $FD
.C:c084  A2 20       LDX #$20       ; 32 line segments
.C:c086   .gv_loop1:
.C:c086  A0 00       LDY #$00       ; start offset at 0
.C:c088   .gv_loop2:
.C:c088  A9 18       LDA #$18       ; bit pattern for 2pixel wide line
.C:c08a  91 FC       STA ($FC),Y    ; store bit pattern in bitmap
.C:c08c  C8          INY
.C:c08d  98          TYA
.C:c08e  29 07       AND #$07       ; every 8 lines ...
.C:c090  D0 F6       BNE .gv_loop2
.C:c092  98          TYA        ; ... add 16 to offset to
.C:c093  69 10       ADC #$10       ; skip 2 columns
.C:c095  A8          TAY
.C:c096  CA          DEX
.C:c097  F0 13       BEQ .gov
.C:c099  8A          TXA
.C:c09a  29 03       AND #$03       ; every 4 line segments ...
.C:c09c  D0 EA       BNE .gv_loop2
.C:c09e   .gh:
.C:c09e  A5 FC       LDA $FC        ; ... add $140 (8 * 25) to vector
.C:c0a0  69 40       ADC #$40       ; to get one "row" down
.C:c0a2  85 FC       STA $FC
.C:c0a4  A5 FD       LDA $FD
.C:c0a6  69 01       ADC #$01
.C:c0a8  85 FD       STA $FD
.C:c0aa  D0 DA       BNE .gv_loop1

         ; draw vertical grid line endings:
         ; --------------------------------
.C:c0ac   .gov:
.C:c0ac  A9 18       LDA #$18
.C:c0ae  A2 02       LDX #$02       ; every ending is 3 pixels high
.C:c0b0   .gov_loop:
.C:c0b0  9D 05 E0    STA $E005,X    ; store at different offsets
.C:c0b3  9D 1D E0    STA $E01D,X    ; in bitmap, see explanation
.C:c0b6  9D 35 E0    STA $E035,X    ; for squares above
.C:c0b9  9D 4D E0    STA $E04D,X
.C:c0bc  9D 40 EB    STA $EB40,X
.C:c0bf  9D 58 EB    STA $EB58,X
.C:c0c2  9D 70 EB    STA $EB70,X
.C:c0c5  9D 88 EB    STA $EB88,X
.C:c0c8  CA          DEX
.C:c0c9  10 E5       BPL .gov_loop

         ; draw horizontal grid lines:
         ; ---------------------------
.C:c0cb  A9 0B       LDA #$0B       ; load vector with
.C:c0cd  85 FC       STA $FC        ; start address of the
.C:c0cf  A9 E0       LDA #$E0       ; first grid line
.C:c0d1  85 FD       STA $FD
.C:c0d3  A2 20       LDX #$20       ; 32 line segments
.C:c0d5   .gh_loop1:
.C:c0d5  A0 00       LDY #$00       ; start offset at 0
.C:c0d7   .gh_loop2:
.C:c0d7  A9 FF       LDA #$FF       ; all bits set for horizontal lines
.C:c0d9  91 FC       STA ($FC),Y    ; store bit pattern in bitmap
.C:c0db  C8          INY
.C:c0dc  98          TYA
.C:c0dd  29 01       AND #$01       ; every other line ...
.C:c0df  D0 F6       BNE .gh_loop2
.C:c0e1  98          TYA
.C:c0e2  69 06       ADC #$06       ; ... add 6 to offset to get to the
.C:c0e4  A8          TAY        ; next block
.C:c0e5  CA          DEX
.C:c0e6  F0 13       BEQ .goh
.C:c0e8  8A          TXA
.C:c0e9  29 07       AND #$07       ; every 8 line segments ...
.C:c0eb  D0 EA       BNE .gh_loop2
.C:c0ed  A5 FC       LDA $FC        ; ... add $3C0 (8 * 75) to vector
.C:c0ef  69 C0       ADC #$C0       ; to get three "rows" down
.C:c0f1  85 FC       STA $FC
.C:c0f3  A5 FD       LDA $FD
.C:c0f5  69 03       ADC #$03
.C:c0f7  85 FD       STA $FD
.C:c0f9  D0 DA       BNE .gh_loop1

         ; draw horizontal grid line endings:
         ; ----------------------------------
.C:c0fb   .goh:
.C:c0fb  A9 1F       LDA #$1F       ; bit pattern for 5 pixel wide line end
.C:c0fd  A2 01       LDX #$01       ; 2 pixels high
.C:c0ff   .goh_loop:
.C:c0ff  9D 03 E0    STA $E003,X    ; store at different offsets ...
.C:c102  9D C3 E3    STA $E3C3,X
.C:c105  9D 83 E7    STA $E783,X
.C:c108  9D 43 EB    STA $EB43,X
.C:c10b  49 E7       EOR #$E7       ; switch to right line end
.C:c10d  9D 4B E0    STA $E04B,X    ; and store ...
.C:c110  9D 0B E4    STA $E40B,X
.C:c113  9D CB E7    STA $E7CB,X
.C:c116  9D 8B EB    STA $EB8B,X
.C:c119  49 E7       EOR #$E7       ; switch back to left line end
.C:c11b  CA          DEX
.C:c11c  10 E1       BPL .goh_loop

         ; setup hardware to show graphics:
         ; --------------------------------
.C:c11e  A9 94       LDA #$94       ; select top bank for VIC-II
.C:c120  8D 00 DD    STA $DD00      ; address space ($C000 - $FFFF)
.C:c123  A9 3B       LDA #$3B       ; activate hires bitmap mode
.C:c125  8D 11 D0    STA $D011
.C:c128  A9 3D       LDA #$3D       ; select $0c00 for start of bitmap
.C:c12a  8D 18 D0    STA $D018      ; colors, $2000 for start of bitmap
.C:c12d  A9 01       LDA #$01       ; set border color to white
.C:c12f  8D 20 D0    STA $D020
.C:c132   .hl_end:
.C:c132  D0 FE       BNE .hl_end    ; --- and never come back (loop)

C++ using SFML, 708 706 685 bytes
-23 bytes thanks to Zacharý
#include<SFML\Graphics.hpp>
#define V =RectangleShape({6.f,300.f});r
#define H =RectangleShape({300.f,6.f});r
#define S(a,b).setPosition(a,b);
#define D(c,d)a.setPosition(c,d);w.draw(a);
using namespace sf;int main(){RenderWindow w(VideoMode(300,300),"");RectangleShape r[8];r[0]V[1]V[2]V[3]V[4]H[5]H[6]H[7]H[0]S(-3,0)r[1]S(97,0)r[2]S(197,0)r[3]S(297,0)r[4]S(0,-3)r[5]S(0,97)r[6]S(0,197)r[7]S(0,297)CircleShape a(40);a.setFillColor(Color::Black);while(w.isOpen()){Event e;while(w.pollEvent(e))if(e.type==Event::Closed)w.close();w.clear(Color::White);for(int i=0;i<8;++i){r[i].setFillColor(Color(128,128,128));w.draw(r[i]);}D(110,10)D(210,110)D(210,210)D(110,210)D(10,210)w.display();}}

Open a window where you see that :

Window is 300x300, so grid boxes are 100x100, and the dot have a 40 pixel radius, so the number of covered pixels is 40*40*3.14 ( something like 5024 ). So it respects the 40%-80% area filling rule.
If you want to run it by yourself, you should add this line : w.setFramerateLimit(60), to avoid your computer to heat up

IIRC, you don't need a space between your macros and the definitions in this case. I'll work on extreme macro abuse right now.

@Zacharý I can for S and D, but not for V and H

Here's what I got, because when I tested that macro-equal-thing on g++ (albeit with a different program) it worked, so you may have to change a few things here and there for it to work in MSVC: https://pastebin.com/0uTHTc4Q

TikZ + TeX (plain TeX) 86 83 bytes
\input tikz\def~{)circle()(}\tikz\filldraw[step=2]grid(6,6)(,~3,~5,~5,3~3,5~,);\bye

Try online

Explanation
I recently found out that plain TeX saves you lots of bytes when compared to LaTeX. Started this one from scratch.

\input tikz loads the TikZ-library
\def~{)circle()(} defines the single-character-macro ~ (~ is the only active character!) as shorthand for some text. This macro does not need any argument to be supplied. Note that the circle has a radius of 1 if we do not supply an argument.
\tikz starts the picture
\filldraw draws the grid and fills the circles at the same time
[step=2] leaves, by default, 2 units of space between gridlines, cf. here
grid(6,6) produces the 3x3 grid
(,~3,~5,~5,3~3,5~ paints the circles by using the ~-macro and, in the end, opens another co-ordinate with the last use of ~. Note that, whenever the digit 1 could've been supplied in a co-ordinate entry, I just left it out. This happened three times, with (1,1), (3,1), and (5,1).
,) completes this co-ordinate. It becomes, in total, (,) which is equivalent to (1,1), see above, but does not have any effect on the output.
; ends the filldraw command. We could leave it out if we surrounded everything between \tikz and \bye with curly braces but this would be two bytes more resulting in a grand total of one byte more.
\bye ends the TeX document.

What about GRAY?
This is a little tricky but I believe that rgb(x,x,x) with 0≤x≤255 are 256 shades of gray. Basically, black is the darkest gray available.

Output

Applesoft Basic, 405 291 bytes
0data51,29,73,51,73,73,51,73,29,73
1hgr:hcolor=3:fori=0to123:hplot0,i to123,i:next
2hcolor=0:fori=18to84step22:hplot18,i to84,i:hploti,18toi,84:next
3fori=1to5:reada,b:a=a+.5:b=b+.5:gosub8:next:end
8forr=1to9:hplota,b:forn=0to6.3step.1:y=sin(n)*r:x=cos(n)*r:hplottoa+x,b+y
9next:next:return

0data... locates the circles. 1hgr... draws white background. 2hcolor... draws grid. 3for..read.. gets data & calls the circle subroutine. (.5 is added at various points because of quirky Apple II drawing logic). 8for... to 9..return draws circles. (circle code adapted from hoist-point.com). Verified at https://www.calormen.com/jsbasic/

version 1: 405 bytes
a crappy version that fits in a tweet!

Here is my answer in Tcl/Tk 17 lines, 582 Characters:
proc box {sz x y} {return [list [expr {$x*$sz+4}] [expr {$y*$sz+4}] [expr {($x+1)*$sz+4}] [expr {($y+1)*$sz+4}]]}
proc oval {sz x y} {return [list [expr {$x*$sz+($sz*0.8)+4}] [expr {$y*$sz+($sz*0.8)+4}] [expr {($x+1)*$sz-($sz*0.8)+4}] [expr {($y+1)*$sz-($sz*0.8)+4}]]}
set SIZE 100
canvas .c -width [expr {$SIZE*3+8}] -height [expr {$SIZE*3+8}]
pack .c
set y 0
foreach row {{0 1 0} {0 0 1} {1 1 1}} {
set x 0
foreach dot $row {
.c create rectangle {*}[box $SIZE $x $y] -fill {} -outline grey -width 1
if {$dot} {
.c create oval {*}[oval $SIZE $x $y] -fill black
}
incr x
}
incr y
}

Output:

The dots you are using only cover around 28% of the white fill of each cell, the requirement is to cover with the range 40-80%. (It may even cost less bytes to fill ~79% by removing the *0.8s)

Comparing to my answer, yours is code-bowling!

Bash + feh, 391 bytes
xxd -ps -r<<G|
425a6839314159265359e008ecc700015afa48d2582082482013004000002008010008b000f920484289880d30a069a1919310294a1a50d19a8c4e5394d2689a8b5057b493c759b1374c26136c94fae37ceb40bf7c73fdc3d07e6f95f69d8440f2040209a5281d49e9fbbcb692bc07725bcbcb62a6e106510a635089343060c630bc075b6dacd71ae32b7c6ec64b0b03e0457c912b22f31613c9305e46558c2bd589e9313b2602f6e3aff177245385090e008ecc70
G
bzcat|feh -

The hex string in the heredoc is a PNM image of the logo, compressed with BZip2 at level 9. xxd -ps -r converts it into binary data, and it is subsequently piped into bzcat (a BZip2 utility that decompresses data on STDIN and prints it on STDOUT). feh - opens the image viewer feh using data from STDIN.
feh - is two bytes shorter than the traditional ImageMagick display program.
If any bash wizards can help me figure out a way to use the raw binary data instead of having to encode it in hex (echo -ne wasn't working right), I'd be forever grateful.
Thanks to kundor for 2 bytes

Wow, that's pretty creative!

You can get rid of the spaces around the pipes and << to save four bytes or so.

you can put bzcat... right after the pipe and save a newline

You can save 121 bytes by using base64 and removing the padding. First line becomes: base64 -d<<H|bzcat|feh -.

Processing, 221 bytes
There must be a way to bring this down, but its not jumping out at me.  Maybe I can find something with coding some of the oft-used constants to variables, like 30, 15, 25, etc.  Possibly it can be done with the rectMode(CENTER) taken out, but I think the coordinates will on average get longer, canceling out the savings.
size(30,30);background(255);rectMode(CENTER);fill(0);rect(15,5,5,5);rect(15,25,5,5);rect(25,15,5,5);rect(5,25,5,5);rect(25,25,5,5);stroke(99);noFill();rect(5,15,10,30);rect(25,15,10,30);rect(15,5,30,10);rect(15,25,30,10);

Ungolfed:
//open the tiny window
size(30,30);
//set the background to white
background(255);
//all rectangle coordinates will be relative to their centers, rather than the top left corner
rectMode(CENTER);
//black
fill(0);
//5 rectangles for the glider. Rectangle because "rect" is shorter than "ellipse"
rect(15,5,5,5);
rect(15,25,5,5);
rect(25,15,5,5);
rect(5,25,5,5);
rect(25,25,5,5);
//set border color to gray. 99 because its noticeably gray without being a 3-digit number, saving a byte
stroke(99);
//rectangles will be hollow from now on
noFill();
//draw 4 boxes for the grid lines.
//Draw a box around the left 1/3 of the window, then the right 1/3,
//then the top 1/3, and lastly the bottom 1/3
rect(5,15,10,30);
rect(25,15,10,30);
rect(15,5,30,10);
rect(15,25,30,10);

Output:
I can't do anything about the gray border in the image, that is the smallest Processing will let me open a window, but the actual image is in the center.  If I were to save that frame of the image, it would only be the 30x30 bit in the middle.

Not bad! It doesn't break any rules, the gray around the image is technically a border!

Lua + LÖVE, 264 characters
l=love
g=l.graphics
g.setLineStyle("rough")function l.draw()g.setBackgroundColor(255,255,255)for i=0,8 do
x=i%3*11+1
y=(i-i%3)/3*11+1
g.setColor(127,127,127)g.rectangle("line",x,y,11,11)if("15678"):find(i)then
g.setColor(0,0,0)g.circle("fill",x+5,y+5,4)end
end
end

Sample output:

Lua + LÖVE, 241 characters
l=love
g=l.graphics
function l.draw()g.setBackgroundColor(255,255,255)for i=0,8 do
x=i%3*10+1
y=(i-i%3)/3*10+1
g.setColor(127,127,127)g.rectangle("line",x,y,10,10)if("15678"):find(i)then
g.setColor(0,0,0)g.circle("fill",x+5,y+5,3)end
end
end

The question owner not answered yet the anti-aliasing question and some other solutions also have additional gray shades added by the anti-aliasing, so for now this shorter one looks also acceptable.
Sample output:

Javascript, 187 bytes (174+13) 205 bytes (192+13)

a=40,w=80,x=c.getContext`2d`;c=(X,Y)=>{x.beginPath();x.arc(X*a+20,Y*a+20,15,0,7);x.fill()};c(1,0),c(2,1),c(0,2),c(1,2),c(2,2);i=4;while(i--)x.strokeRect((i&1)*a,(i&2)*20,w,w)
<canvas id=c>

Ungolfed
a = 40,w = 80,
x = c.getContext `2d`;
c = (X, Y) => {
    x.beginPath();
    x.arc(X * a + 20, Y * a + 20, 15, 0, 7);
    x.fill()
};
c(1, 0), c(2, 1), c(0, 2), c(1, 2), c(2, 2);
i = 4;
while (i--) x.strokeRect((i&1)*a, (i&2)*20, w, w)

191 bytes

@ThePirateBay nice! I could reduce even further to 187

imagegen, 167 154 55 bytes (non-competing)
Edit 2: Added grid draw function
Edit 1: Fixed bug where you needed spaces to separate rectangle fill functions. Removed those spaces

31x31 F^G910R013,3,5R023,13,5R03,23,5R013,23,5R023,23,5

Running
To run, click the link above to the Github repository and read the usage section. After running imagegen, just paste the code into the console and hit enter. The program will generate an image to out.png in the same folder as the EXE.
Just started working on a new image generation golf language today to help me get used to C#. The score will continue to decrease as I add more features (and the answer will hopefully get less boring).
Explanation

Fill image with white background
Draw gray grid
Draw each black rectangle individually

Code explanation

31x31 sets image size. If you leave this out it will default to 600x400
F^ Fills the image with the color '^'. If you look in ColorCodes.txt in the Github repository, you will see this corresponds to white. This fills the white background.
G910 Draws a grid of lines 10 pixels apart with the color '9' which corresponds to #999999 - gray. This draws the gray grid
R013,3,5,5 is the fill rectangle function. I use the color '0' (black), and it draws this rectangle at (13,3) with size 5x5.

This is still a major work in progress, just started a couple hours ago

Not bad, good luck on your language! +1

Tcl/Tk, 173
pack [canvas .c -bg #FFF]
time {.c cr l [incr i 10] 10 $i 40 -f gray;.c cr l 10 $i 40 $i -f gray} 4
lmap x\ y {2 1 3 2 1 3 2 3 3 3} {.c cr o ${x}2 ${y}2 ${x}8 ${y}8 -f #000}

Tcl/Tk, 188
pack [canvas .c -bg #FFF]
lmap i {2 12 22 32} {.c cr l 2 $i 32 $i -f gray;.c cr l $i 2 $i 32 -f gray}
lmap x {13 23 3 13 23} y {3 13 23 23 23} {.c cr o $x $y [incr x 8] [incr y 8] -f #000}

HTML & CSS, 113 bytes
I started working on this version because of Octopus' similar answer.I've mainly done away with excessive elements and replaced them with CSS-generated pseudo elements.

th{font:5em/.37''}th::after{content:'\2022

{new Frame(){public void paint(Graphics g){int x=20,y=40,z=60;g.fillOval(x,0,x,x);g.fillOval(0,y,x,x);g.fillOval(x,y,x,x);g.fillOval(y,y,x,x);g.fillOval(y,x,x,x);g.setColor(Color.gray);g.drawRect(0,0,z,z);g.drawRect(0,0,z,y);g.drawRect(0,0,z,x);g.drawLine(x,0,x,z);g.drawLine(y,0,y,z);}}.setVisible(2>1);};

Opens a window as small as the OS allows that can be dragged open. Also the image is partially hidden behind the window border:

Here's how it would look like without the border

Java 11 (OpenJDK), 265 bytes
This is just a golfed version of @mindoverflow's answer, full credit goes to him.
The problem is just that I can't comment yet.
()->{new Frame(){public void paint(Graphics g){int x=20,y=40,z=60,i=3;g.fillOval(x,0,x,x);g.fillOval(y,x,x,x);for(;i-->0;g.fillOval(i*x,y,x,x));g.setColor(Color.gray);for(i=4;i-->1;g.drawRect(0,0,z,i*x));g.drawLine(x,0,x,z);g.drawLine(y,0,y,z);}}.setVisible(2>1);};

It looks just like @mindoverflow`s, but by wrapping a couple of method calls into for-loops you can save 45 bytes.

Java 10, 242 236 bytes
import java.awt.*;v->new Frame(){{add(new Panel(){public void paint(Graphics g){for(int i=9,t;i-->0;g.setColor(Color.BLACK),g.fillOval(t%3*10+1,t/3*10+1,8,8),g.setColor(Color.GRAY),g.drawRect(i%3*10,i/3*10,10,10))t=i>4?i:1;}});show();}}

-6 bytes thanks to @ceilingcat.
Screenshot of the actual 30x30 pixels result:

Screenshot of the ten times larger 300x300 result:

Explanation:
import java.awt.*;            // Required import for almost everything
v->                           // Method with empty unused parameter and Frame return-type
  new Frame(){                //  Create the Frame
   {                          //   In an inner code-block:
     add(new Panel(){         //    Add a Panel we can draw on:
       public void paint(Graphics g){
                              //     Overwrite its paint method:
         for(int i=9,t;i-->0  //      Loop `i` in the range (9, 0]:
             ;                //        After every iteration:
              g.setColor(Color.BLACK),
                              //         Set the color to black
              g.fillOval(     //         Draw a solid circle:
                t%3*10+1,,    //          At x-coordinate t%3*10 + 1
                t/3*10+1,     //          At y-coordinate t//3*10 + 1
                8,8),         //          Of size 8x8 pixels (80%)
              g.setColor(Color.GRAY),
                              //         Then change the color to gray
              g.drawRect(     //         Draw an open square:
               i%3*10,i/3*10, //          At x,y-coordinate i%3*10, i//3*10
               10,10))        //          Of size 10x10 pixels
           t=i>4?             //       If `i` is 5, 6, 7 or 8:
              i               //        Set `t` to `i`
             :                //       Else:
              1;}});          //        Set `t` to 1 instead
     show();}}                //    And afterwards show the Frame

05AB1E, 192 bytes
„P230D2•1Ù!}c¡”·?I%Úré$(₅È,ï₂¥%_ŸŸ7ÞÈŸÀā5ΩQKÓ<g‹öÖKζΩlá>ÑzHŠ@UY∍ŸµHÔy¥¥çì’hιιkΔ¶Bα±‘êÕá5jθô∊7”ËôAÓÞªx—Ú‘AyQ½ÝYÇD≠z!ÄŸ—'¡Uγ¥!ε+¿þn0^ƵнƵ%X₃8нÜEÃĀ82Îx'IΣн∍óιêZ±иoŽÐθ³ÿIèÉœRuÎ0DBh_ĆûÚ‹ÈûÉW1Ò₁•3в1»

Try it online! Outputs as a plain PGM.
Output (enlarged)

Actual output size is 30x30.

„P230D2•...•3в1»  # trimmed program
„P2               # push literal
   30             # push literal...
     D            # twice
      2           # push literal
             в    # push list of base-10 values of base...
            3     # literal...
             в    # digits of...
       •...•      # 427541225056090484779390842559962277535356179915664563570852766785644784557400927981185964800417173067134497361607731953969389383383710765821962940570130871145015821269287935868419374755244532042313674055574776583016673806369314382290002772542995095921556681406703587909310388486054711832330497506948107550678784337269482395576308349400348694552486117428881164944824595892240209460307603873442854698425661641130046746477556925533...
              1   # push literal
               »  # join stack by newlines, joining lists on the stack by spaces
                  # implicit output

does converting the dots to perfect squares help with compression?

@Razetime I already noticed, thanks for pointing that out though!

CSS+HTML, 31+95=126 bytes
This is a merge of the solutions from Octopus and Uriel. I figured Uriel solution could benefit from using • instead of using div, and Octopus solution could benefit from using the border attribute instead of setting it in CSS.
So, in the spirit of xkcd/927 I made another answer instead of commenting to either of them.

td{line-height:.4;font-size:3em

•
•
•••

"You will display the emblem with graphical output but no ASCII art."

SVG, 487 449 355 301 Bytes
Compressed Version:
<svg><g id=g><path d=M1,1L1,13L13,13L13,1Z id=r fill=none stroke=gray /><use href=#r x=12 /><use href=#r x=24 /></g><use href=#g /><use href=#g y=12 /><use href=#g y=24 /><circle id=c cx=19 cy=7 r=5 /><use href=#c x=12 y=12 /><use href=#c x=-12 y=24 /><use href=#c x=0 y=24 /><use href=#c x=12 y=24 />

Special Thanks:
@Meyer - Thanks for taking off 54 bytes!
@ovs - Thanks for bringing up compliance issue with radius of circle
Uncompressed Version Code Snippet:

<svg>
    <g id=g>
        <path d=M1,1L1,13L13,13L13,1Z id=r fill=none stroke=gray />
        <use href=#r x=12 />
        <use href=#r x=24 />
    </g>
    <use href=#g />
    <use href=#g y=12 />
    <use href=#g y=24 />
    <circle id=c cx=19 cy=7 r=5 />
    <use href=#c x=12 y=12 />
    <use href=#c x=-12 y=24 />
    <use href=#c x=0 y=24 />
    <use href=#c x=12 y=24 />

CSS+HTML = 13+83 = 96 bytes

td{width:21px

⬛
⬛
⬛⬛⬛

Thanks to @mbomb007
CSS+HTML = 38+83 = 121 bytes [No Unicode]
CSS
p{width:8px;height:8px;background:#000

HTML

Tested on:
Chrome 72.0.3626.121

CSS+HTML = 38+87 = 125  bytes [No Unicode]
CSS
p{width:8px;height:8px;background:#000

HTML

Tested on:
Chrome 72.0.3626.121

Nice. How about this for 87 bytes?

@mbomb007 Nice. This is your version updated. Only 83 bytes. But I think it's against the rules Each grid-box in the grid will be the same size.

Your first one does the exact same thing (see your first image), but this shorter version doesn't appear to be square, which is a requirement.

SVG/CSS, 700 bytes

rect{fill:white;stroke:grey;stroke-width:1}
<svg width=48 height=48 ><rect width=16 height=16 /><rect x=16 width=16 height=16 /><circle cx=24 cy=8 r=6 stroke=black stroke-width=1 fill=black /><rect x=32 width=16 height=16 /><rect y=16 width=16 height=16 /><rect x=16 y=16 width=16 height=16 /><rect x=32 y=16 width=16 height=16 /><circle cx=40 cy=24 r=6 stroke=black stroke-width=1 fill=black /><rect y=32 width=16 height=16 /><circle cx=8 cy=40 r=6 stroke=black stroke-width=1 fill=black /><rect x=16 y=32 width=16 height=16 /><circle cx=24 cy=40 r=6 stroke=black stroke-width=1 fill=black /><rect x=32 y=32 width=16 height=16 /><circle cx=40 cy=40 r=6 stroke=black stroke-width=1 fill=black /></svg>

Welcome to PPCG! I'm not an expert in SVG, but I'm fairly certain that you can remove some unnecessary bytes (indentation, plus the <-- something --> lines that appear to be comments).

Here is a golfed version at 700 bytes using svg and css. Feel free to edit it in.


text:
Place a glider!
This:

is a Glider.
In Conway's Game of Life, the glider is a famous pattern that rapidly traverses across the board. For today's challenge, we are going to draw an ASCII art Game of Life Board, and place a glider on it.
The board we are starting with is this:
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|

This board is made up entirely of pipes | and underscores _, and is 10x10. You must write a program or function that takes in two integers, 'x' and 'y', and outputs this same board with a glider at those coordinates. For example, if you had a glider at position (1, 1) (0-indexed), you must output the following:
|_|_|_|_|_|_|_|_|_|_|
|_|_|*|_|_|_|_|_|_|_|
|_|_|_|*|_|_|_|_|_|_|
|_|*|*|*|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|

You can assume that the glider will never be placed out of bounds, so both x and y will always be in the [0-7] range. You may also choose to take the coordinates 1-indexed, but you must specify this in your answer. In this, case the inputs will always be in the [1-8] range. Here are some examples (all 0-indexed):
0, 0:
|_|*|_|_|_|_|_|_|_|_|
|_|_|*|_|_|_|_|_|_|_|
|*|*|*|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|

7, 7:
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|*|_|
|_|_|_|_|_|_|_|_|_|*|
|_|_|_|_|_|_|_|*|*|*|

7, 4:
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|*|_|
|_|_|_|_|_|_|_|_|_|*|
|_|_|_|_|_|_|_|*|*|*|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|

5, 2:
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|*|_|_|_|
|_|_|_|_|_|_|_|*|_|_|
|_|_|_|_|_|*|*|*|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|
|_|_|_|_|_|_|_|_|_|_|

As usual, you may take your IO in any reasonable format. This includes, but is not limited to a string with newlines, an array of strings, a 2d array of strings, or writing to a file/STDOUT. You may also choose what order to take x and y in.
Since this is code-golf, standard loopholes are banned, and make the shortest code that you can!

Can we change which corner identifies the glider's position?

@StephenS No, the coordinates should Identify where the top left corner of the glider starts.

Related.

the glider is a famous pattern that slowly traverses across the boar.. Slowly? It is the fastest diagonal moving object in GoL. It reaches 1/4 of the speed of light.

@Christoph, good point,we can observe that it's length does not appear to contract in the direction of travel, but I don't know any way of measuring it's mass at relativistic speeds.

MATL, 35 32 30 bytes
20*+'|_'5E:21:I$)42b' 34'Q+(

The code contains unprintable chars. Input is 0-based. Try it online!

V, 31, 30 bytes
10O±°_|ÀGjjÀ|3r*kr*kh.Í*ü_/|&

Try it online!
Hexdump:
00000000: 3130 4fb1 b05f 7c1b c047 6a6a c07c 3372  10O.._|..Gjj.|3r
00000010: 2a6b 722a 6b68 2ecd 2afc 5f2f 7c26       *kr*kh..*._/|&

This takes input as program arguments, and 1-indexed.
Explanation:
10O                         " On the following 10 lines, insert:
   ±°_                      "   10 '_' characters
      |                     "   And a '|'
       <esc>                " Return to normal mode
            ÀG              " Go to the a'th line
              jj            " Move down two lines
                À|          " Go to the b'th column
                  3r*       " and replace the next 3 characters with asterisks
                     k      " Move up a line
                      r*    " And replace this char with an asterisk
                        kh  " Move up a line and to the left
                          . " And repeat the last change we performed (replace with asterisk)
                            "
Í                           " On every line, substitute:
 *                          "   An asterisk
  ü                         "   OR
   _                        "   An underscore
    /                       " With:
     |&                     "   A bar followed by the matched pattern

Java 10, 165 144 138 135 129 127 125 bytes
x->y->{var r="";for(int i=0,j;++i<11;r+="|\n")for(j=0;j<10;)r+=i==x&j==y|i==x+1&++j==y|i==x+2&j<y+3&j>=y?"|*":"|_";return r;}

1-indexed.
-14 bytes thanks to @ceilingcat.
Explanation:
Try it here.
x->y->{               // Method with two integer parameters and String return-type
  var r="";           //  Result-String, starting empty
  for(int i=0,j;i<10  //  Loop over the rows:
      ;               //    After every iteration:
       r+="|\n")      //     Append a "|" and newline to the result-String
    for(j=0;++j<11;)  //   Inner loop over the columns:
      r+=i==x&j==y|i==x+1&++j==y|i==x+2&j<y+3&j>=y?
                      //    If this coordinate should contain a '*'
        "|*"          //     Append "|*" to the result-String
       :              //    Else:
        "|_");        //     Append "|_" to the result-String
  return r;}          //  Return the result-String

Explanation has different code than your answer?  Look at first assignment to r.

@Computronium Oops, thanks for noticing, fixed. The bye-count was correct, explanation was correct, TIO-link was correct, but the actual answer was still the old incorrect one..

Perl 5, 67 bytes
sub{map{($_-join('',@_)~~[1,12,20..22]?"|*":"|_")."|\n"x/9$/}0..99}

Try it online!
Input Parameters: y, x

Jelly, 37 35 bytes
ȷ2b1
Ḍ+“£Æßæç‘Ṭ+¢s⁵j3;0$€Fṭ0ị“_*¶|”

Try it online!
How it works
ȷ2b1                             - the literal [1,1,1,1,...,1,1,1] with 100 elements
Ḍ+“£Æßæç‘Ṭ+¢s⁵j3;0$€Fṭ0ị“_*¶|”   - input (x,y)
Ḍ                                - convert (x,y) to 10*x+y
 +                               - add, to get the five "*" positions,
  “£Æßæç‘                        - the literal [2,13,21,22,23]
         Ṭ                       - return an array with those positions as truthy elements
          +¢                    - Now we format: pad to length 100 with the above literal
            s⁵j3                 - add newlines (represented by 3) to each set of 10
                ;0$€F            - add pipes (represented by 0) to each
                     ṭ0          - add a 0 to the beginning
                       ị“_*¶|”   - index into the string “_*¶|”

How do you type/generate your programs?

@RobotCaleb : generally copy-pasting from the Jelly codepage. I run them at the TIO nexus when not at my main machine, and a clone of the Jelly repository at my main computer.

Python 2, 151 bytes
Will golf more.
def f(x,y):r,x=[list('|_'*10+'|')for i in[1]*10],x*2;r[y][x+3]=r[y+1][x+5]=r[y+2][x+1]=r[y+2][x+3]=r[y+2][x+5]='*';print'\n'.join(''.join(i)for i in r)

Try it online!

If you're willing to switch to Python 3, you can save 3 bytes by using [*'|_'*10+'|'] instead of the list() call.

Perl 6, 88 bytes
->\x,\y{(^10 »*»i X+ ^10).map:{<|* |_>[$_!=
(1-2i|2-i|0|1|2)+x+y*i+2i]~"|
"x(.re==9)}}

Complex numbers are used to represent the coordinates.
^10 »*» i X+ ^10 generates the grid of all complex numbers with integer components from zero through nine.
Returns a list of strings, each one holding one line.

Interesting, I didn't know that Perl utilizes non-ASCII. What does » do? How is it encoded?

It's just Unicode's RIGHT POINTING GUILLEMET, U+00BB, encoded in UTF-8.  In Perl 6 it can just as well be written as two angle brackets, >>, but that's the same number of bytes as the guillemet so for golfing I prefer the latter as it looks a little classier IMHO.  As for what it does, it transforms the operator it surrounds into a "hyperoperator" that applies pairwise to the lists or values on either side.  Here, it multiplies each element of the range 0-9 by i, giving 0, i, 2i, ..., 9i.

JavaScript (ES6), 99 bytes
x=>y=>eval('for(i=0,o="";i<101;o+=((d=i-x-y*10)==1|d==12|d>19&d<23?"|*":"|_")+(++i%10?"":`|\n`))o')

Takes input via currying: f(5)(2) for x=5, y=2. Coordinates are zero-indexed.
Test Snippet

f=
x=>y=>eval('for(i=0,o="";i<101;o+=((d=i-x-y*10)==1|d==12|d>19&d<23?"|*":"|_")+(++i%10?"":`|\n`))o')

xi.oninput=yi.oninput=_=>O.innerHTML=f(xi.value)(yi.value)
O.innerHTML=f(xi.value=5)(yi.value=2)

x: <input id="xi" type="number" min="0" max="7">,
y: <input id="yi" type="number" min="0" max="7">

You can save 1 byte if split the sum https://tio.run/##DcrRCoMgFIDhdzmwpZlD29WqUy8yWJY2HNIZNaLAd3fBz3f1f8xm1nHx358czOCCnMm6NGHasT2wdZsJLJtoYR5VQQhQ@0YrXZNAZtHLXR65VhxRR3tSRtvqx9U25b2DmEMF8QXFOQvhL1p1AFUfn3PPKeNppHml4G6B3mxiip/xOv0B

Ruby, 87 bytes
->x,y{[a=(b='|_')*10+?|]*y+%w(|_|*|_ |_|_|* |*|*|*).map{|r|b*x+r+b*(7-x)+?|}+[a]*(7-y)}

Try it online!

APL (Dyalog Extended), 40 bytes
'*'@((⍳2)∘×¨⎕∘+¨⍸⊤1 5 3)⊢'|',⍨10 20⍴'|_'

Try it online!
A full program, accepting x and y as input.
Calculate coordinates, and uses @ to modify them.

Python 3, 97 bytes
def f(x,y):
 b=(["|_"]*10+["|\n"])*10
 for c in[1,13,22,23,24]:b[c+11*y+x]="|*"
 return"".join(b)

Try it online!

Mathematica, 115 113 bytes
x(a="|_"~Table~10~Table~10;(a[[##]]="|*")&@@(#+x)&/@({{0,1,2,2,2},{1,2,0,1,2}});""<>Riffle[#<>"|"&/@a,"\n"])

where
This takes input in {row, col} format, and is 1-indexed, but can be turned into 0-indexed without adding bytes.
Some notes:

\n is a newline character, takes 1 byte.
 is \[Function], takes 3 bytes.
 is \[Transpose], takes 3 bytes.

Note that "array of string" is allowed, so I can just remove Riffle, gives
Mathematica, 98 97 bytes
x(a="|_"~Table~10~Table~10;(a[[##]]="|*")&@@(#+x)&/@({{0,1,2,2,2},{1,2,0,1,2}});#<>"|"&/@a)

Haskell, 96 bytes
r=[0..9]
x#y=['|':(r>>=(\i->[last$'_':['*'|elem(i-x,j-y)$zip[1,2,0,1,2][0,1,2,2,2]],'|']))|j<-r]

Takes in two integers (x and y) and returns a list of Strings, i.e. a 2D list of type [[Char]].

Test suite:
import System.Environment

main :: IO ()
main = do
    args <- getArgs
    let (x, y) = (read $ args !! 0, read $ args !! 1)
    mapM_ putStrLn (x#y)

Python 2, 133 bytes
x,y=input()
a=[[z for z in'_'*10]for o in'|'*10]
b=a[y+2]
a[y][x+1]=a[y+1][x+2]=b[x]=b[x+1]=b[x+2]="*"
for b in a:print o+o.join(b)+o

Try it online!

Zsh, 85 bytes
try it online!
for i (2 13 21 22 23)G[i+$1$2]=*
for i ({1..100})printf \|${G[i]:-_}&&((i%10))||<<<\|

Saved 12 bytes by moving the glider char * assignments to a function.
Saved another 4 bytes by removing the function and adding a ternary to the printf stuff (at the expense of readability!). Saved another 7 by inlining some data instead of constructing an array. Saved another 10 by simplifying some expressions. Removed {} for another -1. Saved 11 by using parameter expansion ${name:-word} instead of convoluted ascii logic.
130 bytes 118 bytes 114 bytes 107 bytes 97 bytes 96 bytes

JavaScript, 98 bytes
Based on this answer
x=>y=>eval('for(i=0,o="";i<101;o+=(d=i-x-y*10)==1|d==12|d>19&d<23?"|*":"|_",o+=++i%10?"":`|\n`)o')

We can save 1 byte if use o+=a,o+=b instead of o+=(a)+(b)
Try it:

f=
x=>y=>eval('for(i=0,o="";i<101;o+=(d=i-x-y*10)==1|d==12|d>19&d<23?"|*":"|_",o+=++i%10?"":`|\n`)o')

xi.oninput=yi.oninput=_=>O.innerHTML=f(xi.value)(yi.value)
O.innerHTML=f(xi.value=5)(yi.value=2)

x: <input id="xi" type="number" min="0" max="7">,
y: <input id="yi" type="number" min="0" max="7">

JavaScript, 101 bytes
This answer may be more efficient with another pattern
x=>y=>eval("for(i=0,s='';i<101;s+=[1,12,20,21,22].includes(i-x-y*10)?'|*':'|_',s+=++i%10?'':`|\n`)s")

Try it:

f=
x=>y=>eval("for(i=0,s='';i<101;s+=[1,12,20,21,22].includes(i-x-y*10)?'|*':'|_',s+=++i%10?'':`|\n`)s")

xi.oninput=yi.oninput=_=>O.innerHTML=f(xi.value)(yi.value)
O.innerHTML=f(xi.value=5)(yi.value=2)

x: <input id="xi" type="number" min="0" max="7">,
y: <input id="yi" type="number" min="0" max="7">

SOGL, 23 bytes
LIΖ|_ΟL∙.«."¾'┼ΞΧ⌠²‘5nž

note: this expects input to be 1-indexed
Explanation:
LI                       push 11
  Ζ|_                    push "|" and "_"
     Ο                   make an altrenates string [with 11 separators, which are "|" and parts "_"]
      L∙                 get an array of 10 of those
        .«               take input and multiply by 2 (x pos)
          .              take input (y pos)
           "¾'┼ΞΧ⌠²‘     push "_|*|__|_|**|*|*" - the glider in the map
                    5n   split into an array with items of length 5
                      ž  replace in the 1st [grid] array at positions [inp1*2, inp2] to 2nd array [glider]

Charcoal, 28 bytes
ＵＯ²¹χ|_Ｊ×Ｎ²Ｎ“ "}ＩyE%%mＬＢＭφ/”

Try it online! Link to verbose mode for description.

I'm disappointed that Charcoal isn't, well... ASCII-only ;)

You can remove the |_ before the \n to save a compressed byte. (I tried several different ways of printing the glider but none saved any bytes.)

Python 2, 97 bytes
def f(x,y):
 b=(["|_"]*10+["|\n"])*10
 for c in[1,13,22,23,24]:b[c+11*y+x]="|*"
 return"".join(b)

Try it online!

QBasic, 113 107 bytes
Uses 0-indexing.
INPUT x,y
s=11*y+x
FOR i=1TO 110
?"|"+CHR$(95+53*((i=s+2)+(i=s+14)-(i>s+22)*(i<s+26))+85*(i/11=i\11));
NEXT

We model the grid as 110 |s each followed by some character, for which we calculate the ASCII code:

Every 11th character is a newline (character code 95 - 85 = 10)
If we calculate the start index of the glider as s = 11 * y + x, then indices s + 2, s + 14, s + 23, s + 24, and s + 25 are asterisks (character code 95 - 53 = 42)
The rest are underscores (character code 95)

Original version using LOCATE (1-indexed):
INPUT x,y
CLS
FOR i=0TO 9
FOR j=0TO 9
?"|_";
NEXT
?"|
NEXT
LOCATE y,2*x+2
?"*
LOCATE,2*x+4
?"*
LOCATE,2*x
?"*|*|*

Canvas, 24 bytes
|_Ａ×|＋Ａ＊ *¶  *¶***∙＊┘«╶╋

Try it here!
Takes 1-indexed position in form
x
y

as input.
Only beaten by dzaima's SOGL answer at time of writing.

05AB1E, 25 (or 24?) bytes
„|_ти„|*•A|,•₂вI+ǝTôJ€Ć»

0-based indexing.
Try it online or verify all test cases.
Could be 1 byte less (the J) if we could take the input-coordinates joined together as single integer:
Try it online or verify all test cases.
Explanation:
„|_            # Push string "|_"
   ти          # Repeat it 100 times as list of strings
„|*            # Push string "|*"
   •A|,•       # Push compressed integer 681976
        ₂в     # Convert it to base-26 as list: [1,12,20,21,22]
          IJ   # Push the input-pair, and join it together to an integer
            +  # Add it to each value in the list [1,12,20,21,22]
„|*          ǝ # Insert the "|*" string at those (0-based) indices to the list of "|_"
Tô             # Split the list into 10 parts of 10 strings each
  J            # Join each inner list together to a string
   €           # Map over each line:
    Ć          # Enclose; append its own head (the "|")
     »         # Join the list of strings by newlines
               # (After which the result is output implicitly)

See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •A|,• is 681976 and •A|,•₂в is [1,12,20,21,22].

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes